﻿<?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>IT博客网-玄铁剑-文章分类-ASP.NET相关</title><link>http://www.cnitblog.com/MartinYao/category/4539.html</link><description>欢迎您的到来...</description><language>zh-cn</language><lastBuildDate>Fri, 04 Apr 2008 09:58:15 GMT</lastBuildDate><pubDate>Fri, 04 Apr 2008 09:58:15 GMT</pubDate><ttl>60</ttl><item><title>custom GridView pager</title><link>http://www.cnitblog.com/MartinYao/articles/41587.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Thu, 27 Mar 2008 14:44:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/41587.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/41587.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/41587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/41587.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/41587.html</trackback:ping><description><![CDATA[<p>&lt;PagerTemplate&gt; <br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblPage" runat="server" Text="Page:" ForeColor="blue"&gt;&lt;/asp:Label&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblCurrentPage" runat="server" Font-Bold="true" Text="&lt;%# ((GridView)Container.NamingContainer).PageIndex + 1 %&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ForeColor="red"&gt;&lt;/asp:Label&gt;/<br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblPageCount" runat="server" Font-Bold="true" Text="&lt;%# ((GridView)Container.NamingContainer).PageCount %&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ForeColor="black"&gt;&lt;/asp:Label&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnFirstPage" runat="server" CommandName="Page" CommandArgument="First" Text="First"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != 0 %&gt;"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnPreviousPage" runat="server" Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != 0 %&gt;" Text="Prev"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CommandName="Page" CommandArgument="Prev"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnNextPage" runat="server" CommandName="Page" CommandArgument="Next" Text="Next"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != ((GridView)Container.NamingContainer).PageCount - 1 %&gt;"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnLastPage" runat="server" CommandName="Page" CommandArgument="Last" Text="Last"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != ((GridView)Container.NamingContainer).PageCount - 1 %&gt;"&gt;&lt;/asp:LinkButton&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&lt;/PagerTemplate&gt;<br>&lt;PagerStyle CssClass="GridPager"&nbsp; HorizontalAlign="Center"/&gt;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/41587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-03-27 22:44 <a href="http://www.cnitblog.com/MartinYao/articles/41587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>10 ASP.NET Performance and Scalability Secrets</title><link>http://www.cnitblog.com/MartinYao/articles/40978.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 15 Mar 2008 14:28:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/40978.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/40978.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/40978.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/40978.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/40978.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ASP.NET pipeline optimizationASP.NET process configuration optimizationThings you must do for ASP.NET before going liveContent Delivery NetworkCaching AJAX calls on browserMaking best use of...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/40978.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/40978.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-03-15 22:28 <a href="http://www.cnitblog.com/MartinYao/articles/40978.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>性能的设计与开发</title><link>http://www.cnitblog.com/MartinYao/articles/40314.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Fri, 29 Feb 2008 14:11:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/40314.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/40314.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/40314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/40314.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/40314.html</trackback:ping><description><![CDATA[<div class=postbody>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">性能问题应该从系统设计时期开始考虑，并延续到系统的生命期终止之时。</span></span></span></span></span></span><span style="FONT-SIZE: 12pt"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">具有可伸缩性的系统是指当系统的负载增加一倍，系统需要的资源也同样增加一倍。说起来简单，但在现实环境中确难以做到。由于管理并发用户的开销的增长、锁事务的增长、一致性读负载的增加、操作系统负载的增加、</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">低效的</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">SQL或索引设计导致的过高的I/O等等因素，会导致系统资源的消耗的增长远大于一倍。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">破坏可伸缩性的因素：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.低效的应用程序设计、实施和配置 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.硬件部分的规模不合适 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.软件部分的限制 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.硬件部分的限制&nbsp;</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">系统的结构可分为硬件和软件两部分：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">硬件部分包括：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">CPU、内存、I/O子系统和网络模块。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">软件部分包括：管理用户接口、实现商业逻辑、管理用户请求和资源分配、管理数据和事务。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">在设计系统时，应该考虑以下几个问题：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">系统将支持多少用户？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户的交互方式是什么？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户所处的位置？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">网络的速度怎样？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户将访问多少数据？有多少数据是只读访问？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户对响应时间的要求？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户是否需要</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">24小时服务？ </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">是否所有的修改需要实时完成？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">应用程序设计原则：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">设计简单性原则：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.如果表的设计复杂到没有人能够完全的理解，那么表的设计可能是比较差的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.如果SQL语句过长以致于优化程序无法优化该语句，那么SQL语句的设计、事务和表的设计一定存在问题。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.如果表的相同列上被重复索引，那么索引的设计可能是有问题的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.如果提交的查询没有限定，以致无法迅速的将结果返回给在线用户，那么用户接口或事务的设计是有问题的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">5.如果数据库的调用被许多层软件从应用逻辑中抽象出来，那么，软件开发的方法可能存在问题。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据建模：应当注意，不要在非核心数据单元上花费过多的时间。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">表和索引的设计：选择合适的列进行索引、选择索引类型、注意索引的代价、关注索引中列的顺序。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">一个表上如果有</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3个索引，那么当进行INSERT/UPDATE/DELETE操作时，会比不带索引的表慢大约10倍。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">组合索引中，选择性高的列在前查询时需要的</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">I/O更少。选择性低的列在前，有助于代排序操作的查询。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">SQL执行效率： </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据库连接管理：应避免没有必要的过多连接。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据库游标管理：使用</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">cursor和绑定变量，尽量避免硬分析，较少软分析。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">硬分析：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">sql语句第一次提交，并在共享池中无法找到。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">软分析：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">sql语句第一次提交，但是可以在共享池中找到相同的语句。&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">实施新的应用程序：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">切换方式包括两种：</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">Big Bang Approach（所有用户一次性转移到新的系统上）和Trickle Approach（用户分多次转移到新的系统上）。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">性能清单列表：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.设置MAXINSTANCES, MAXDATAFILES，MAXLOGFILES，MAXLOGMEMBERS和 MAXLOGHISTORY的值高于预期值。避免系统的增长导致必须重建控制文件。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.设置BLOCK SIZE和优化模式与开发环境中相同。如果测试环境中的所有SQL语句的执行计划都是正确的，可以测试环境中的统计信息导入到正式库中。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.尽量少修改初始化参数。除了SGA的组成部分和归档目录的设置，其他初始化参数尽量保持默认值，可以为以后性能优化留下一定的余地。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.通过设置数据库对象的存储参数来管理BLOCK的争用。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">5.所有的sql语句应该被优化。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">6.验证中间层软件和程序采用高效的方式连接数据库。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">7.验证sql语句有效的利用游标。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">8.确认所有方案的对象从开发环境移植到了产品数据库中。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">9.一旦完成系统的切换，建立数据库和操作系统统计信息的基线。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">10.发现最先出现的瓶颈。</span></span></span></span></span></span><span style="FONT-SIZE: 10pt"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span></span></p>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/40314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-02-29 22:11 <a href="http://www.cnitblog.com/MartinYao/articles/40314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>防止重复登陆控制方案</title><link>http://www.cnitblog.com/MartinYao/articles/39104.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 20 Jan 2008 10:34:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/39104.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/39104.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/39104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/39104.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/39104.html</trackback:ping><description><![CDATA[<font face="Courier New" color=#0000ff size=2>
<p><br>方案一：</p>
<p>In web.config (StateServer mode, with a one minute timeout to make testing easier):</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc 0?>
    <tbody>
        <tr>
            <td><font face="Courier New, Courier, mono" color=#0000ff size=2>&lt;sessionState <br>mode="StateServer"<br>stateConnectionString="tcpip=127.0.0.1:42424"<br>sqlConnectionString="data source=127.0.0.1;user id=sa;password=letmein"<br>cookieless="false" <br>timeout="1" <br>/&gt;</font></td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p>In Global.asax.cs:</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td><font face="Courier New, Courier, mono" color=#0000ff size=2>protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)<br>{<br><font color=#ff0000>// Let's write a message to show this got fired---</font><br>Response.Write("SessionID: " +Session.SessionID.ToString() + "User key: " +(string)Session["user"]); <br>if(Session["user"]!=null) <font color=#ff0000>// e.g. this is after an initial logon</font><br>{ <br>string sKey=(string)Session["user"];<br><font color=#ff0000>// Accessing the Cache Item extends the Sliding Expiration automatically</font><br>string sUser=(string) HttpContext.Current.Cache[sKey];<br>}<br>}</font></td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p>In your Login Page "Login" button handler:</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td width=650><font face="Courier New, Courier, mono" color=#0000ff size=2>private void Button1_Click(object sender, System.EventArgs e)<br>{ <br><font color=#ff0000>//validate your user here (Forms Auth or Database, for example)<br>// this could be a new "illegal" logon, so we need to check<br>// if these credentials are already in the Cache </font><br>string sKey=TextBox1.Text+TextBox2.Text;<br>string sUser=Convert.ToString(Cache[sKey]);<br>if (sUser==null || sUser==String.Empty){<br><font color=#ff0000>// No Cache item, so sesion is either expired or user is new sign-on<br>// Set the cache item and Session hit-test for this user---</font><br>TimeSpan SessTimeOut=new TimeSpan(0,0,HttpContext.Current.Session.Timeout,0,0);<br>HttpContext.Current.Cache.Insert(sKey,sKey,null,DateTime.MaxValue,SessTimeOut,<br>&nbsp;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable,null);<br>Session["user"]=TextBox1.Text+TextBox2.Text;<br><font color=#ff0000>// Let them in - redirect to main page, etc.</font><br>Label1.Text="&lt;Marquee&gt;&lt;h1&gt;Welcome!&lt;/h1&gt;&lt;/marquee&gt;";<br><br>}<br>else<br>{<br><font color=#ff0000>// cache item exists, so too bad... </font><br>Label1.Text="&lt;Marquee&gt;&lt;h1&gt;&lt;font color=red&gt;ILLEGAL LOGIN ATTEMPT!!!&lt;/font&gt;&lt;/h1&gt;&lt;/marquee&gt;";<br>return;<br>} </font><font face="Courier New, Courier, mono">
            <p><font color=#0000ff size=2><br>}</font></p>
            <p></font>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br>方案二：</p>
<p>public&nbsp;virtual&nbsp;void&nbsp;Application_Start(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>//&nbsp;reset&nbsp;the&nbsp;mailer&nbsp;indicator&nbsp;<br>Application["MailerStatus"]&nbsp;=&nbsp;"All&nbsp;Mailings&nbsp;Complete";&nbsp;</p>
<p>//&nbsp;initialize&nbsp;a&nbsp;datatable&nbsp;for&nbsp;users&nbsp;online&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;new&nbsp;DataTable();&nbsp;<br>objUserTable.Columns.Add("SessionID",System.Type.GetType("System.Guid"));&nbsp;<br>objUserTable.Columns.Add("PeopleID",System.Type.GetType("System.Int32"));&nbsp;<br>objUserTable.Columns.Add("ShowDetail",System.Type.GetType("System.Boolean"));&nbsp;<br>DataColumn[]&nbsp;pk&nbsp;=&nbsp;new&nbsp;DataColumn[1];&nbsp;<br>pk[0]&nbsp;=&nbsp;objUserTable.Columns[0];&nbsp;<br>objUserTable.PrimaryKey&nbsp;=&nbsp;pk;&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>}&nbsp;</p>
<p>/**////&nbsp;<br>///&nbsp;The&nbsp;Session_Start&nbsp;event&nbsp;adds&nbsp;user&nbsp;session&nbsp;information&nbsp;to&nbsp;<br>///&nbsp;Application["UserTable"].&nbsp;<br>///&nbsp;<br>public&nbsp;virtual&nbsp;void&nbsp;Session_Start(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>Application.Lock();&nbsp;<br>//Application.Lock&nbsp;();&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;(DataTable)Application["UserTable"];&nbsp;<br>DataRow&nbsp;objRow&nbsp;=&nbsp;objUserTable.NewRow();&nbsp;<br>Guid&nbsp;objGuid&nbsp;=&nbsp;Guid.NewGuid();&nbsp;<br>objRow[0]&nbsp;=&nbsp;objGuid;&nbsp;<br>Session["PfSessionID"]&nbsp;=&nbsp;objRow[0];&nbsp;<br>objRow[1]&nbsp;=&nbsp;0;&nbsp;<br>objRow[2]&nbsp;=&nbsp;false;&nbsp;<br>objUserTable.Rows.Add(objRow);&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>Application.UnLock();&nbsp;<br>}&nbsp;</p>
<p><br>/**////&nbsp;<br>///&nbsp;The&nbsp;Session_End&nbsp;event&nbsp;deletes&nbsp;user&nbsp;session&nbsp;information&nbsp;from&nbsp;<br>///&nbsp;Application["UserTable"].&nbsp;<br>///&nbsp;<br>public&nbsp;virtual&nbsp;void&nbsp;Session_End(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>Application.Lock();&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;(DataTable)Application["UserTable"];&nbsp;<br>objUserTable.Rows.Find((Guid)Session["PfSessionID"]).Delete();&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>Application.UnLock();&nbsp;<br>}</p>
<p><br>方案三：<br>&nbsp;&nbsp;&nbsp; protected void Login_Click(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到Cache中的给定Key的值&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sUser = Convert.ToString(Cache[sKey]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检查是否存在&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sUser == null || sUser == String.Empty)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Session["username"] = sName;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Cache中没有该Key的项目，表明用户没有登录，或者已经登录超时&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TimeSpan 表示一个时间间隔，获取系统对session超时作的设置值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //（如果考虑到允许用户再次登陆的时间小于session超时时间，可将此值设小）&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TimeSpan SessTimeOut = new TimeSpan(0, 0, System.Web.HttpContext.Current.Session.Timeout, 0, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这里为了演示，把Cache保存时间间隔设置为了20秒<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan SessTimeOut = new TimeSpan(0, 0, 0, 20, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Insert(<br>&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;&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;&nbsp; sKey, <br>&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;&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;&nbsp; sKey, <br>&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;&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;&nbsp; null, <br>&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;&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;&nbsp; DateTime.MaxValue, <br>&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;&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;&nbsp; SessTimeOut,<br>&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;&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;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable, <br>&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;&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;&nbsp; null<br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //启动Timer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Timer1.Enabled = true;</p>
<p>Label1.Text = "你好！" + sName + "欢迎光临";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在Cache中发现该用户的记录，表示已经登录过，禁止再次登录&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = "对不起，你的用户身份已登陆";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception ex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = ex.Message;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; protected void Logout_Click(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为了测试方便，设置了这个从Cache中移出登陆信息的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Remove(sKey);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = Session["username"] + " 的用户登陆信息已从Cache清除!";<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; protected void TimerRunning_Tick(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Session["username"] != null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到Cache中的给定Key的值&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sUser = Convert.ToString(Cache[sKey]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan SessTimeOut = new TimeSpan(0, 0, 0, 20, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sUser != null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Remove(sKey);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Insert(<br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null, <br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateTime.MaxValue, <br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SessTimeOut,<br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable, <br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null<br>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Timer1.Enabled = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br><br></p>
</font>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/39104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-01-20 18:34 <a href="http://www.cnitblog.com/MartinYao/articles/39104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Templates</title><link>http://www.cnitblog.com/MartinYao/articles/32427.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 25 Aug 2007 14:40:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/32427.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/32427.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/32427.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/32427.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/32427.html</trackback:ping><description><![CDATA[<table class=multicol>
    <tbody>
        <tr>
            <td class=innercol style="WIDTH: 100%" vAlign=top>
            <h1>Design Templates</h1>
            <p>Download these professionally designed Starter Kits and XHTML templates to get you started creating compelling, standards-compliant Web pages with ASP.NET 2.0. There are four Starter Kits below. These Starter Kits are complete web site solutions using ASP.NET&#8217;s latest controls (tree view control, grid view control, detail view control, login control, site map path controls and more). There are another 5 XHTML templates below. These are individual XHTML strict Web page templates (available each in two different colors) that are accompanied by design philosophy whitepapers that describe each template&#8217;s Section 508 conformance, page layout with CSS (page layouts using CSS floats or tables), Web page accessibility best practices and Web standard resources. All nine templates below are easily customizable and extendable to fit your own content.</p>
            <p>For introduction to the ASP.NET Master Pages template set, <a href="http://msdn2.microsoft.com/zh-cn/asp.net/aa336603"><u><font color=#0000ff>click here</font></u></a>.</p>
            <h3>ASP.NET Design Starter Kit Templates</h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl02',this);" href="http://download.microsoft.com/download/3/1/3/3134bdea-cae1-4795-a685-d48140264a08/templatecommercecs0106.vsi"><img alt=Commerce src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.commerce.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl03',this);" href="http://download.microsoft.com/download/3/1/3/3134bdea-cae1-4795-a685-d48140264a08/templatecommercecs0106.vsi"><strong><font color=#0000ff><u>Visual C# Commerce Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/0/e/2/0e201d64-1aee-4ffb-abfc-1fa24cecf757/templatecommerceVB0106.vsi"><u><font color=#0000ff>Visual Basic Commerce Design</font></u></a></strong></p>
                        <p>The "Commerce" template features three columns of content in a centered fixed/fluid layout. No layout tables, just CSS. The template showcases the new ASP.NET Treeview control for displaying hierarchical lists of items and shows how to skin it as part of a theme. The use of "Commerce" is not limited to online shop scenarios. Its three themes are well suited for other purposes as well, such as fan sites or even a personal web site. Section 508 accessibility, a dedicated print style sheet and several themed tabular controls, such as GridView and DetailsView complement its feature set. Included themes: Magnolia, Jazz, Snow.</p>
                        <p><em>Visual C# File Download Size: 1.0 MBVisual Basic File Download Size: 1.0 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/commerce.doc"><u><font color=#0000ff>"Commerce" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl04',this);" href="http://download.microsoft.com/download/2/3/2/2328890d-8ca1-4b7f-a138-08fca8a244bd/templatepersonalcs0106.vsi"><img alt=Personal src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.personal.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl05',this);" href="http://download.microsoft.com/download/2/3/2/2328890d-8ca1-4b7f-a138-08fca8a244bd/templatepersonalcs0106.vsi"><strong><font color=#0000ff><u>Visual C# Personal Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/9/3/3/933adec5-f5d4-4501-b552-0e825a0cf0bf/templatepersonalVB0106.vsi"><u><font color=#0000ff>Visual Basic Personal Design</font></u></a></strong></p>
                        <p>Best suited for a personal or blog-style web site, this template has a hybrid fixed/fluid layout that organizes the content in two columns, with a horizontal menu at the top. It is a pure CSS layout that doesn&#8217;t rely on tables for arranging page content. Themes can be selected by the user and their preference will be restored upon the next visit by taking advantage of the new ASP.NET profile feature. The template contains a Section 508-compliant sample form, a print style sheet and a themed GridView control sample. Included themes: Green, Brown, Red.</p>
                        <p><em>Visual C# File Download Size: 1.0 MB Visual Basic File Download Size: 1.0 MB </em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/personal.doc"><u><font color=#0000ff>"Personal" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl06',this);" href="http://download.microsoft.com/download/9/8/0/980dc514-5e91-4608-b40c-c3aea45b00cb/templatecorporatecs0106.vsi"><img alt=Corporate src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.corporate.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl07',this);" href="http://download.microsoft.com/download/9/8/0/980dc514-5e91-4608-b40c-c3aea45b00cb/templatecorporatecs0106.vsi"><strong><font color=#0000ff><u>Visual C# Corporate Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/e/f/2/ef2f6d60-4cc0-4e9d-a22d-5930999db677/templatecorporateVB0106.vsi"><u><font color=#0000ff>Visual Basic Corporate Design</font></u></a></strong></p>
                        <p>This template has been specifically designed to accommodate large amounts of content. It features a three-column, centered layout with a fixed with. Navigating the site is done though two correlated ASP.NET Menu controls and the SiteMapPath control. Using CSS, the page content is placed before the navigation in the XHTML source to improve accessibility and search-engine ranking. Placeholders and themes for including personalization features, such as the new Login control, are provided. The template is Section 508-compliant, contains a dedicated print style sheet and a themed GridView sample control. Included themes: Granite, Sand, Paper. </p>
                        <p><em>Visual C# File Download Size: 1.7 MBVisual Basic File Download Size: 1.0 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/corporate.doc"><u><font color=#0000ff>"Corporate Design" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl08',this);" href="http://download.microsoft.com/download/7/0/6/7061c0be-477a-44a3-ac02-a75ffd172f2d/templatesmallbusinesscs0106.vsi"><img alt="Small Business" src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.smallbiz.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl09',this);" href="http://download.microsoft.com/download/7/0/6/7061c0be-477a-44a3-ac02-a75ffd172f2d/templatesmallbusinesscs0106.vsi"><strong><font color=#0000ff><u>Visual C# Small Business Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/e/3/6/e36ac37c-92fd-462b-b55e-7d5bcfdaf69f/templatesmallbusinessVB0106.vsi"><u><font color=#0000ff>Visual Basic Small Business Design</font></u></a></strong></p>
                        <p>This template features two columns that are arranged in a centered, fixed-width layout. It is best suited for a small to medium-sized site. Its themes are widely different from each other, demonstrating the visual potential of ASP.NET themes and skins. Additional highlights include: table-less layout; source-ordered XHTML, placing the page content before the navigation elements; Section 508-compliant sample form; ASP.NET Menu and SiteMapPath controls; varying header images; dedicated print style sheet that formats the pages for output in a printer; themed GridView control sample. Included themes: Fruits, Forest, Literature. </p>
                        <p><em>Visual C# File Download Size: 1.3 MBVisual Basic File Download Size: 1.3 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/small_business.doc"><u><font color=#0000ff>"Small Business" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <p>These templates illustrate high quality design principles combined with standards driven code. Each template is XHTML 1.1 Strict, Section 508/WCAG conforming and works across many browsers. The accompanying documentation describes how the template was built, the thought processes, tradeoffs, and workarounds necessary to build it. It also contains instructions on how to easily customize and change the templates for use in your site. You will find these HTML templates to be an excellent starting point when beginning to build out dynamic, data-driven Web sites with Microsoft Visual Studio and Microsoft Visual Web Developer. We hope they'll give both designers and developers deep insight into their creation process while providing you with a workable starting point.</p>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl10',this);" href="http://download.microsoft.com/download/0/b/1/0b155bb5-6890-4af0-94a7-7b5b87c16796/basic06.msi"><img alt=Basic src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-basic.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl11',this);" href="http://download.microsoft.com/download/0/b/1/0b155bb5-6890-4af0-94a7-7b5b87c16796/basic06.msi"><strong><u><font color=#0000ff>Basic</font></u></strong></a><br>
                        <p><strong>The Basic template</strong> <em>File Download Size: 603 KB</em></p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are fixed width</li>
                        </ul>
                        <p><em>File Download Size: 603 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/basic.doc"><u><font color=#0000ff>"Basic" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl12',this);" href="http://download.microsoft.com/download/d/8/2/d8291134-ac4d-45cc-b7eb-446564bb095e/fun06.msi"><img alt=Fun src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-fun.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl13',this);" href="http://download.microsoft.com/download/d/8/2/d8291134-ac4d-45cc-b7eb-446564bb095e/fun06.msi"><strong><u><font color=#0000ff>Fun</font></u></strong></a><br>
                        <p><strong>The Fun template</strong> </p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are stretchy
                            <li>This layout uses absolute positioning</li>
                        </ul>
                        <p><em>File Download Size: 393 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/fun.doc"><u><font color=#0000ff>"Fun" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl14',this);" href="http://download.microsoft.com/download/1/f/d/1fd59802-643c-49d2-9586-26216d6840e8/grid06.msi"><img alt=Grid src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-grid.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl15',this);" href="http://download.microsoft.com/download/1/f/d/1fd59802-643c-49d2-9586-26216d6840e8/grid06.msi"><strong><u><font color=#0000ff>Grid</font></u></strong></a><br>
                        <p><strong>The Grid template</strong> </p>
                        <ul>
                            <li>3 column layout is used in the header section
                            <li>2 column layout is used for the body section
                            <li>All the columns are "stretchy"</li>
                        </ul>
                        <p><em>File Download Size: 570 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/grid.doc"><u><font color=#0000ff>"Grid" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl16',this);" href="http://download.microsoft.com/download/c/7/a/c7a07f0e-4778-4d73-a163-f873a06b18ed/rounded06.msi"><img alt=Rounded src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-rounded.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl17',this);" href="http://download.microsoft.com/download/c/7/a/c7a07f0e-4778-4d73-a163-f873a06b18ed/rounded06.msi"><strong><u><font color=#0000ff>Rounded</font></u></strong></a><br>
                        <p><strong>The Rounded template</strong> </p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are fixed width</li>
                        </ul>
                        <p><em>File Download Size: 495 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/rounded.doc"><u><font color=#0000ff>"Rounded" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl18',this);" href="http://download.microsoft.com/download/f/d/e/fdea6532-984c-43ec-b41f-ea6b92d33edb/simple06.msi"><img alt=Simple src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-simple.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl19',this);" href="http://download.microsoft.com/download/f/d/e/fdea6532-984c-43ec-b41f-ea6b92d33edb/simple06.msi"><strong><u><font color=#0000ff>Simple</font></u></strong></a><br>
                        <p><strong>The Simple template</strong> </p>
                        <ul>
                            <li>3 column layout
                            <li>The side columns are fixed width. The middle column is "stretchy"</li>
                        </ul>
                        <p><em>File Download Size: 420 KB</em></p>
                        <p>For more information</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/32427.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-08-25 22:40 <a href="http://www.cnitblog.com/MartinYao/articles/32427.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SSIS  Interface not registered</title><link>http://www.cnitblog.com/MartinYao/articles/29894.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 16 Jul 2007 00:28:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/29894.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/29894.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/29894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/29894.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/29894.html</trackback:ping><description><![CDATA[<div class=postsub>
<h2><a id=_ctl0__ctl0__ctl0__ctl0_Month__ctl0_postlist__ctl0_EntryItems__ctl0_PostTitle href="http://www.vsteamsystemcentral.com/cs/blogs/applied_team_system/archive/2006/03/30/53.aspx"><u><font color=#0000ff>Interface Not Registered / Class Not Registered Error in SSIS</font></u></a></h2>
<p><font face=Verdana color=#000080>I ran into a couple errors recently when trying to create new SQL Server Integration Services (SSIS) projects. One error stated:</font></p>
<p><font face="Times New Roman" color=#000000 size=4>Failed to save package file "C:\Documents and Settings\Administrator\Local Settings\Temp\1\tmp2B.tmp" with error 0x80040155 "Interface not registered".<br></font><font face=Verdana color=#000080></font></p>
<p><font face=Verdana color=#000080>The other stated:</font></p>
<font face=Verdana color=#000080>
<p><font size=4><font face="Times New Roman" color=#000000>Failed to save package file "C:\Documents and Settings\Administrator\Local Settings\Temp\1\tmp2B.tmp" with error 0x80040154 "Class not registered".</font><br></font><font face=Verdana color=#000080></font></p>
<p><font face=Verdana color=#000080>These errors were encountered on a new laptop and a new virtual PC. Both had Visual Studio 2005 installed, so I suspected some sort of conflict. On the VPC, I loaded VS 2005 first, followed by SQL Server 2005 Developer. I noticed the client tools didn't install, although I thought I'd checked that box. To correct, I executed the setup for client tools. When completed, my client tools were available and creating an SSIS project succeeded.</font></p>
<p>But the laptop wasn't so simple. I thought "I have a solution!" and went about re-installing the client tools, but it didn't correct the issue! </p>
<p>So I popped on Google and searched for similar issues. I found a helpful post at MSDN Forums: <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=53694&amp;SiteID=1"><u><font color=#0000ff>http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=53694&amp;SiteID=1</font></u></a>. Executing two RegSvr32 commands did the trick:</p>
<p><font face="Times New Roman" color=#000000 size=4>regsvr32 msxml3.dll<br>regsvr32 msxml6.dll&nbsp;</font></p>
<p>When all else fails, read the instructions!</p>
<p>:{&gt; Andy</p>
</font></div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/29894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-07-16 08:28 <a href="http://www.cnitblog.com/MartinYao/articles/29894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>區域打印</title><link>http://www.cnitblog.com/MartinYao/articles/28622.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:20:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28622.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28622.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28622.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28622.html</trackback:ping><description><![CDATA[<p>&lt;html&gt;<br>&lt;head&gt;打印</p>
<p>&lt;scrīpt language="javascrīpt"&gt;<br>function print()<br>{<br>&nbsp;&nbsp;&nbsp; document.body.innerHTML=document.getElementById('print').innerHTML;</p>
<p>&nbsp;&nbsp;&nbsp; window.print();<br>}<br>&lt;/scrīpt&gt;</p>
<p>&lt;/head&gt;<br>&lt;body&gt;</p>
<p>&lt;form id="form1" runat="server"&gt;<br>&lt;div id="print"&gt;<br>&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;<br>&lt;/div&gt;</p>
<p>&lt;asp:Button ID="butPrint" runat="server" Text="打印" OnClientClick="Print();" CssClass="cmd_print" /&gt;<br>&lt;/form&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</p>
<p>&nbsp;</p>
<p>使用CSS区域打印：</p>
<p>定义一个.noprint的class，将不打印的内容放入这个class内。</p>
<p>&lt;style media=print type="text/css"&gt;&nbsp;&nbsp; <br>.noprint{visibility:hidden}&nbsp;&nbsp; <br>&lt;/style&gt;&nbsp;&nbsp; </p>
<p>&lt;p class="noprint"&gt;将不打印的代码放在这里。&lt;/p&gt;<br>&lt;a href="javascrīpt:window.print()"打印&lt;/a&gt;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:20 <a href="http://www.cnitblog.com/MartinYao/articles/28622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Building Tree View on demand using AJAX</title><link>http://www.cnitblog.com/MartinYao/articles/28364.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:38:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28364.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28364.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28364.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28364.html</trackback:ping><description><![CDATA[<ul>
    <li><a href="http://www.codeproject.com/useritems/TreeViewAjax/ajaxtree.zip">Download source files - 20 Kb</a> </li>
</ul>
<p><img height=453 alt="Sample Image - ajaxtreeview.gif" src="http://www.codeproject.com/useritems/TreeViewAjax/ajaxtreeview.gif" width=512></p>
<h2>Introduction</h2>
<p><span>AJAX or Asynchronous JavaScript and XML is a free framework for quickly creating a new generation of more efficient, more interactive and highly-personalized Web experiences that work across all the most popular browsers. <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is relatively new phenomenon, based on technologies that aren&#8217;t quite new, for creating a way for businesses to interact with customers over the internet.</p>
<p><span>&nbsp;</p>
<p><span>In this article using the current <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> technique,(in it&#8217;s simplest way) to implement an on demand tree view. Since XML is common standard and easy to implement I am using XML in my sample application rather than any database. I have found another article here that to build a <a href="http://www.codeproject.com/aspnet/ajax_treeview.asp">tree view</a> using this <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>.</p>
<p><span>&nbsp;</p>
<h2><span>History: (Skip this section if you are feeling bored)</h2>
<p><span>This technology was introduced first by Microsoft (from my best knowledge) back in 1999, and had been known as &#8220;DHTML / JavaScript web application with remote calls&#8221;. The whole idea of the technology was to allow an internet browser to make an asynchronous HTTP call to remote pages/services, and update a current web page with the received results without refreshing the whole page. By creators&#8217; opinion, this should have improved customers&#8217; experience, making HTTP pages look and feel very similar to Windows applications.</p>
<p>Because the core implementation of this technology was based on internet browser functionality, the usability was very limited at that time. But several years later, the technology has been reborn with new browsers support and massive implementation by such giants as Google, Amazon.com, eBay, etc.</p>
<p>Today, it&#8217;s known as <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>, and considered as a natural part of any dynamic web page with advanced user experience. Many ready made frameworks are also available on the internet like <a href="http://ajax.asp.net/Default.aspx">Microsoft AJAX/ATLAS</a> , <a href="http://www.backbase.com/">Enterprise AJAX</a> are few of them.</p>
<h3>Why <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>:</h3>
<p>This sample application provides a tree view implementation from an XML file. In real scenarios, I have noticed the processing of building the complete tree structure (with many levels-children) and rendering into the browser takes quite long time. After attempting different approaches, one of them was building the tree on demand. But I don&#8217;t like the Post Back (I am sure you too). Then I found is <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is the best bet. By this we could avoid a post back and flickering of the browser rather we&#8216;re just changing the portion of the page hence reducing the traffic in the server and enhance the performance.</p>
<h2><span>Working:</h2>
<p><span>The idea is to build a tree structure using UL and LI tags and using a style sheet and some JavaScript all together give some real effects. On Page Load or any Pre Render event, the Root node will be loaded, from that on click of the plus/minus images you can expand or collapse the tree. You can notice that the child node is generated on the fly (off course you can cache the data source). Since <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is asynchronous, I am displaying an image to indicate that a request is already made to the server.</p>
<h2>Implementation:</h2>
<p>The Code files include </p>
<p><em>TreeSource.XML</em> -&gt; The source file where the tree structure is defined.</p>
<p><span>&lt;?<span>xml<span> <span>version<span>="1.0"<span> <span>encoding<span>="utf-8"<span> <span>?&gt;</p>
<p><span>&lt;<span>Nodes<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>&lt;<span>Node<span> <span>NodeID<span>="0"<span> <span>NodeName<span>="World"<span> <span>ParentID<span>="-1"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>&lt;<span>Node<span> <span>NodeID<span>="1"<span> <span>NodeName<span>="<st1:place w:st="on">North America</st1:place>"<span> <span>ParentID<span>="0"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span>&lt;<span>Node<span> <span>NodeID<span>="13"<span> <span>NodeName<span>="<st1:country-region w:st="on"><st1:place w:st="on">United States</st1:place></st1:country-region>"<span> <span>ParentID<span>="1"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span></p>
<p><em>AJAXroutine.js</em> -&gt; The JavaScript file contain all the required methods to handle the asynchronous calls and standard <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> methods. Java script is the core component in this technology as you can see. It handles the change detection, data request receipt, and placing the data on the page.</p>
<p><span>&nbsp;</p>
<p><span>//To toggle the node image and expand collapse</p>
<p><span>function<span> Toggle(node){</p>
<p><span><span><span><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>// Unfold the branch if it isn't visible</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.nextSibling.style)<span>&nbsp;&nbsp; {<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.childNodes.length &gt; 0)<span>&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.childNodes.item(0).nodeName == "IMG"){</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...</p>
<p><span><span>&nbsp;</p>
<p><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span>The core function for the asynchronous request is</p>
<p><span>//The core function to get the xml http request object</p>
<p><span>function<span> GetXmlHttpObject(){ </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>var objXMLHttp=<span>null</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (window.XMLHttpRequest){</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...</p>
<p><span><span>&nbsp;</p>
<p><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span>The HTML will looks like the following tags.</p>
<p>&lt;ul id="treeUL" class="treeUL"&gt;</p>
<p>&lt;li id='10'&gt;</p>
<p>&lt;span title='World' valign='bottom' onclick="javascript:if (Toggle(this))showContentsEx('divTree0','0','');"&gt;</p>
<p>The client SPAN will be call the following function to invoke the server request</p>
<p><span>/***********************************************************</p>
<p><span>//Builds the query string and invoke a request to the server </p>
<p><span>//using javascript async call</p>
<p><span>/***********************************************************/</p>
<p><span>function<span> showContentsEx(div, str, lmsg){ ...</p>
<p><em>WebForm1.aspx</em>-&gt; The sample .aspx file. It has the root level hard coded in this sample. I have put some styles to look nice. You can build that section also on the fly. </p>
<p><em>WebForm1.aspx.cs</em> -&gt; The code behind of the .aspx file. </p>
<p><em>GetTreeContents.aspx</em> -&gt; Empty in the design mode</p>
<p>GetTreeContents.aspx.cs -&gt; All the client requests are processing this code behind and renders into the browser. When the client http request is made the following scripts will execute on the server and renders the HTML on the browser. </p>
<span>
<p><span>private<span> <span>void Page_Load(<span>object sender, System.EventArgs e)</p>
<p><span>{<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span><span>if<span> (!IsPostBack)</p>
<p><span><span>&nbsp;{</p>
<p>&nbsp;</p>
<p><span>//id passed thro' the url query string</p>
<p><span>string<span> key = Request.QueryString["q"]; </p>
<p><span>&nbsp;</p>
<p><span>if<span> (key != <span>null)</p>
<p><span>{</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//read the xml tree file from the web.config file </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//add the following key in web.config</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&lt;appSettings&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&lt;add key="XmlTree" value="~/TreeSource.XML" /&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String path = Server.MapPath(ConfigurationSettings.AppSettings["XmlTree"]);</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//I use XmlDocument object here to manipulate Xml- You can use other objects</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//You can cache the XmlDocument.OuterXml string</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlDocument xmlTree = <span>new XmlDocument();</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlTree.Load(path);</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.IO.StringReader xmlSR = <span>new System.IO.StringReader(xmlTree.OuterXml);</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//If you want to use database driven tree you skip above codes</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//but the columns should be there in the data table</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataSet ds = <span>new DataSet();</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ds.ReadXml(xmlSR);</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataView dv = ds.Tables[0].DefaultView;</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//filter for the parent</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dv.RowFilter = " ParentID = '" + key + "'";</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//render the brower with a &lt;UL&gt; tag and &lt;LI&gt; list</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//I have used some styles</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write ("&lt;ul class='wTreeStyle'&gt;"); </p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//write all the children here</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>for(<span>int i=0; i &lt; dv.Count; i++)</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//formatted &lt;LI&gt; tag - </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write (<span>string.Format("&lt;li id='{0}'&gt;&lt;span valign='bottom' title='{1}' onclick=\"javascript:if (Toggle(this))showContentsEx('divTree{0}','{0}','');\"&gt;&lt;IMG<span>&nbsp; align='bottom' SRC='plus.gif'&gt; &lt;span class='treeStyleNode' &gt;{1}&lt;/span&gt;&lt;/span&gt;&lt;span id ='divTree{0}'&gt;&lt;/span&gt;&lt;/li&gt;", </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dv[i]["NodeID"].ToString(), dv[i]["NodeName"].ToString())); </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write ("&lt;/ul&gt;"); </p>
<p><span>}<span>&nbsp;.<span><span>..&nbsp;&nbsp;</p>
<p><span><span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>Images -&gt; Loading.gif, plus.gif, minus.gif </p>
<p>Web.config - &gt; for the key</p>
<p><span>&lt;<span>add<span> <span>key<span>="XmlTree"<span> <span>value<span>="~/TreeSource.XML"<span> <span>/&gt;</p>
<p>For the simplicity, I am putting only the required codes since I want to share the implementation, you can customize or enhance in your projects.</p>
<h2>Conclusion:</h2>
<p>This article is aimed to illustrate the simplicity of using the <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> technology in any ASP.NET application. It is up to the developers who want to use the technology in this way. I just did a practical implementation of the technology since the core code is already in place. Currently I tested the application in MS Visual Studio V.7/ MDE 2003 and MS .NET Framework 1.1. And C# - ASP.NET.</p>
<h2>Credits:</h2>
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:38 <a href="http://www.cnitblog.com/MartinYao/articles/28364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Using authentication certificates to connect to web services with Windows form applications</title><link>http://www.cnitblog.com/MartinYao/articles/28363.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:16:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28363.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28363.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28363.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28363.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28363.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/csharp/SSLViaCertification/coolcode_src.zip"><u><font color=#0000ff>Download source - 16.3 Kb</font></u></a> </li>
</ul>
<p><img height=100 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode1.gif" width=130></p>
<h2>Introduction</h2>
<p>This article explains how to use a certificate to connect to, for example, a web service. Do you know the floppies or USB Pens that some banks and IT Companies give to their employees or customers that permit them to connect to services via the internet using a security file and channel? Well, we will learn how to use a similar file in this article, which is divided into six parts:</p>
<ol>
    <li>Environment Preparation
    <li>Server Certificate Request
    <li>Generate the Certificate
    <li>Activate the SSL Security Channel Communication on the IIS Web Server
    <li>Request and Obtain a User Certificate
    <li>Use Certificate with Windows Form </li>
</ol>
<p>For this article I used three different machines:</p>
<ul>
    <li>A Windows 2000 Server with Certification Authority
    <li>Two Windows XP Professional Edition machines, one with IIS Web Server </li>
</ul>
<p>You can use only one Windows XP Professional machine; I use the second machine to ensure that the certification file and the software that I develop are the only two things that I must take away to connect to the web service. Good luck! </p>
<h2>Environment Preparation</h2>
<p>In this section we create the two projects that we will use to try our certification program. I won't explain the two projects in detail; I presume that you are expert enough to create two standard projects with Visual Studio 2005. </p>
<p>All right, start creating a new website project. Choose the ASP.NET Web Service template. Attention! It is important that you use a real web server and not the emulator. Therefore, if you don't have IIS Web Server or something similar installed, install it and then choose the <code>HTTP</code> option from the combo location of the VS Project. For the name of the application, I chose <code>http:<span class=cpp-comment>//localhost/WebServSSL</span></code>. Visual Studio takes care of most of what you need; you only have to configure the website appropriately. If you build the project you'll have just one method in the service, the classic "hello word" example. I suggest we use this for our test, but if you don't like it you can create another function that you prefer. The second project that you must now create is a Windows Application. Create it in the same solution as the Web Service; I called mine <code>testSSLWebServ</code>. In the new project, add a Web Reference from the solution explorer and choose your web service. I called the namespace for the service <code>remoteWebServSSL</code>. All right, the last pass is to add a button on the form and call the unique method of the Web Service that we have. Here 's some code: </p>
<pre lang=cs>remoteWebServSSL.Service srv = new remoteWebServSSL.Service();
MessageBox.Show(<span class=cpp-string>"The Web Service say: "</span> + srv.HelloWorld());</pre>
<p>Important: Some problems can arise from the firewall settings of the machine. Be sure to turn off the firewall; you can configure it in a second time. </p>
<p>Now we can start. </p>
<h2>Server certificate request</h2>
<p>Next, to activate the secure channel on our web site, we must go to the properties of the root Web Server and set the Server Certificate. There's more than one mode to do this; you can choose ones you prefer. I chose to connect to the CA Web Server to ask for the Certificate. Therefore I must create a Certification Request and ask the CA a second time. Click on the Server Certificate button. The certificate wizard will then appear; click Next. In this dialog choose Create a New Certificate and click Next. The only option that you can choose now is "Prepare the request now, but send it later;" again click Next. In this form you must choose the name of the certificate that you will install on your web server. Leave the other parameter as it is and go on. Now write something for the organization and organization unit. Go on. This next step is important: the Common Name required is the name of your machine and is, therefore, the name of the web server that responds to your web server. For example, my machine name is <code>WorkStatio23</code> and the http address that I write when I navigate on my web server is <code>http:<span class=cpp-comment>//WorkStatio23/WebServSSL</span></code>. The Common Name that I must choose is <code>WorkStatio23</code>. Also insert the State/province and City/locality data and then go on. As a last step, save your Certification Request; choose a location and click Next. Read the Request File Summary, click Next and Finish. The Certification Request has now been created. </p>
<h2>Generate the certificate</h2>
<p>Open the generated Text file on your computer from the directory where you chose to save it. Select all the text in the file and copy it. Now it's time to use Windows 2000 Server with the CA. Ensure that your CA is configured to accept the request by the Web Server and that it releases the certificate immediately, without user participation. Connect to the CA Web Server using the related address. For example, my Windows 2000 Server machine is named <code>w2ksrw</code>. Therefore the address to request the certification is <code>http:<span class=cpp-comment>//w2ksrw/CertSrv</span></code>. Next, choose the second option "Request a certificate" and click Next. Choose the "Advanced request" option and click Next again. Choose the second available option: "Submit a certificate request using a base64 encoded PKCS #10 file or a renewal request using a base64 encoded PKCS #7 file." Click Next. Since we already have the Text file code copied to memory, just paste it in the relevant textbox control (the first) and click Next. Now you are ready to download the certificate. Download the two certificates on the result page: "Download CA certificate" and "Download CA certification path." </p>
<h2>Activate the SSL security channel communication on the IIS Web Server</h2>
<p>Select the certificate file with the <em>.p7b</em> extension. For me, this is <em>certnew.p7b</em>. Double click on your file once you find it. Note that the certificate is not valid. Click the "Install certificate" button and then click Next until Finish; confirm the last message and the certificate is now installed. Now finish the activation of the SSL channel on the Web Server. Launch your IIS configuration tool, go on the root Web Server (Default Web Site), right click and select Properties. Return to the Directory Security and re-click the Server Certificate button. Click Next. Note that the options have changed. Choose the "Process the pending request and install the certificate" option and click Next. Browse for the other certificate that you obtain from the CA. Click Next two times and then Finish. Well, you are ready to use your https connection. Try your web server in https and enjoy. For this example, we must enforce the use of the SSL channel on our Web Services. Therefore, on the IIS, go to the properties of WebServSSL, choose the Directory Security tab and click the Edit button. Check the "Require secure channel (SSL)" checkbox and the "Require client certificates" option in the Client Certificates group. Close all windows and try your web services now using your browser.</p>
<p><img height=233 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode2.gif" width=443></p>
<h2>Request and obtain a user certificate</h2>
<p>This step will be fast and simple. Using your browser, go to the Web Server of the CA using the same address (<code>http:<span class=cpp-comment>//w2ksrw/CertSrv</span></code>). Choose the "Request a certificate" option and click Next. Choose the "Advanced request" option and then Next. Leave the "Submit a certificate request to this CA using a form" option selected and click Next. Fill in the Name and the E-Mail fields under Identifying Information with whatever you want and leave the other field as is. For the Intended Purpose field, choose Client Authentication Certificate. For the last setting, set the "Mark keys as exportable" option. Bypass the other field and click Submit. Answer "yes" to the question and finally install the certificate.</p>
<p>Note: In this step, I have encountered problems using the Certification Authority website. The "Downloading ActiveX Control" message did not disappear. If you have the same problem, you can resolve it by installing a fix for the Windows 2000 Advanced Server. Refer to KB323172 of the Microsoft site and find the relative fix <em>q323172_W2K_SP4_X86_EN.exe</em>.</p>
<p><img height=483 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode3.gif" width=409></p>
<h2>Use certificate with Windows form</h2>
<p>You can try more tests with your example, if you'd like, before continuing with my instructions here. After you're satisfied, launch the Internet Explorer browser. Select Tools and go to Internet Options. Select the Content tab and click on the Certificates button. You can immediately see the Personal tab selected and, in the list control, you can see your personal certificate (Andy74 for me). Select the specified certificate and click on the Export button. In the wizard panel choose Next, choose the "Yes, export the private key" option and click Next again. Here you can export only in the PFX format; leave the default option and click Next. Insert a password. For simplicity, I chose a very simple password, "password." Then click Next. Choose where to export the certificate and click Next again. Click Finish and OK in the message box. Now, return to your project in Visual Studio and add this line after the creation of the web reference instance: </p>
<pre lang=cs>srv.ClientCertificates.Add(
new System.Security.Cryptography.X509Certificates.X509Certificate2(
@<span class=cpp-string>"c:\Andy74cert.pfx"</span>, <span class=cpp-string>"password"</span>));</pre>
<p>Obviously, change the path of the certificate where you have saved it. Now on the machine where you have the web services, go to the administrative tools section of the control panel and launch the Server Extensions Administrator. Select File -&gt; Add/Remove Snap-in. On the next window click the Add button, select Certificates and finally click the Add button. Select the Computer Account option and click Next. Leave the "local computer" (the computer this console is running on) option selected and click Finish. Close the active window and finally the OK button. Open the Certificates (Local Computer) node in the tree view, then Trusted Root Certification Authorities and the Certificates node. Right click on the node: All Tasks -&gt; Imports to import another wizard. Click Next. Remember the <em>.p7b</em> certificate that we download from the certification authority in the fourth step. Browse to that file and select it; then click Next. Choose "Place all certificates in the following store" and then the "Trusted Root Certification Authorities" folder; click Next and then Finish. All right, you are ready to try your final application. To really test your application, you can use other machines never used up to now. Copy your application and your <em>.pfx</em> file (certificate) to where you want, but remember to place the certification file in the same folder where you load from the application, or use a parameter to load it from whichever you'd prefer. </p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:16 <a href="http://www.cnitblog.com/MartinYao/articles/28363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Exchange Data Securely without HTTPS/SSL and Implementing Digital Signing in .NET </title><link>http://www.cnitblog.com/MartinYao/articles/28362.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:15:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28362.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28362.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28362.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28362.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28362.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/useritems/SecureDataExchange/SecureDataExchange.zip"><u><font color=#0000ff>Download source code -30.8 Kb</font></u></a> </li>
</ul>
<h2>Introduction</h2>
<p>In this article, I will show you how to exchange data securely with a Web Service without HTTPS/SSL but without compromising the same protection level of HTTPS/SSL. It will also give you a real life example on the implementation and limitation of the important encryption types as well as Digital Signature that are available in .NET Framework.</p>
<h2>Advantages</h2>
<ol>
    <li>No Installation and configuration is required in IIS or in the Client.
    <li>No Deployment issue (Nothing to embed/ship) in the client.
    <li>No Additional Library is required.
    <li>Less easier to implement comparing alternates. </li>
</ol>
<h2>Requirements</h2>
<p>If you are not familiar with Symmetric, Asymmetric Encryption and Digital Signature. I strongly suggest you read the following articles of cp before you move forward, there are also many reference available in the Web on this topic: </p>
<ul>
    <li><a href="http://www.codeproject.com/useritems/Crypto.asp"><u><font color=#0000ff>Cryptography 101 for the .NET Framework</font></u></a>
    <li><a href="http://www.codeproject.com/dotnet/SimpleEncryption.asp"><u><font color=#0000ff>.NET Encryption Simplified</font></u></a>
    <li><a href="http://www.codeproject.com/vb/net/Digital_Signatures.asp"><u><font color=#810081>Implementing Digital Signing in .NET</font></u></a>
    <li><a href="http://www.codeproject.com/dotnet/xmldsiglic.asp"><u><font color=#0000ff>Using XML Digital Signatures for Application Licensing</font></u></a> </li>
</ul>
<p>I have created the solution in Visual Studio 2005 and .NET 2.0 but it can be easily ported to Visual Studio 2003 and .NET 1.1.</p>
<h2>Background</h2>
<h3>Symmetric VS Asymmetric</h3>
<p>I assume that you already know the difference between the Symmetric and Asymmetric encryption. In short, in Symmetric encryption both the parties (the server and client) use the same key to encrypt and decrypt the data. On the other hand, in Asymmetric encryption a pair of key (known as Public/Private Key) is used to encrypt/decrypt. If the data is encrypted with the Private Key the only way to decrypt it is the Public Key and the vice versa. </p>
<h3>Digital Signature</h3>
<p>Digital Signature is also a part of the Public/Private Key implementation. The private key is used to sign the data and public key is used to verify that data has not been tampered. However, Digital signature does not retain the secrecy of the data, which means the data always remain plain (Not encrypted).</p>
<h2>Alternate Solutions</h2>
<ol>
    <li>Share Common Secret Key: The easiest way to implement it to share a common secret key before the Client and Service start communicating.
    <li><strong><a href="http://msdn.microsoft.com/webservices/webservices/building/wse/default.aspx"><u><font color=#0000ff>MS WSE Library</font></u></a></strong>: I found it difficult to implement. Maybe it will look easier to you. </li>
</ol>
<h2>The Solution</h2>
<h3>Initial Proof of Concept</h3>
<p>After going thorough few details of the above mentioned articles and of course, MSDN I came up with the following message exchange between the client and server, which looks pretty, rock solid to me.</p>
<p>
<table style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; BORDER-LEFT: #000000 1px solid; BORDER-BOTTOM: #000000 1px solid" cellSpacing=0 cellPadding=3 border=0>
    <tbody>
        <tr>
            <th style="COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left"></th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Client</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Server</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Hacker</th>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>1.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client creates random Public/Private Key Pair and sends the Server the Public Part</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server in turns creates a random Public/Private Key Pair and returns Public Part to Client.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Hacker can only view both parties Public key.<br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>2.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Client encrypts the target data with received Server Public Key and sends it back to Server.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server receives the encrypted data decrypts it with the previously generated Server Private Key.<br><br>Server Process the Data.<br><br>Server encrypts the process result with the previously received Client Public Key and returns back to Client.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The hacker only has the Server Public key. So it is not possible for the hacker to decrypt the data, as it requires the server private key to decrypt.<br><br><br>Again, the Hacker only has the Client Public key and thus it is not possible for the Hacker to decrypt the process result.<br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>3.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Client receives the encrypted process result and decrypts it with the previously generated Client Private Key.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>&nbsp; </td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: right"><br>4.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Client returns to regular processing<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Server Goes to sleep.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Hacker Cries.<br>&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
<h3>.NET Platform</h3>
<p>In .NET platform, there are different algorithms available for both Symmetric and Asymmetric. I am not going to explain the details of these algorithms as it is beyond the scope of this article. The characteristics are:</p>
<h4>Symmetric Algorithms</h4>
<ul>
    <li>DES: 64 bit encryption - Good.
    <li>RC2: 128 bit encryption - Better.
    <li>Rijndael: 256 bit also known as AES and the most secure algorithm &#8211; The Best.
    <li>TripleDES: 192 bit encryption &#8211; Much better. </li>
</ul>
<h4>Asymmetric Algorithms</h4>
<ul>
    <li>DSA: Does not support Encryption/Decryption. Only allows Digital Signature (So skipping from discussing).
    <li>RSA: Supports both Encryption/Decryption and Digital Signature. But it cannot encrypt/decrypt data more than 117 byte. If you want to encrypt more than 117 byte, it will throw you a Cryptographic Exception with a message that Key not valid for use in specified state. There are many references available in web, which explains this limitation. </li>
</ul>
<h3>Implementation</h3>
<p>As mentioned above the only asymmetric algorithm that supports Encryption/Decryption is RSA, but it has the limitation of data length. Moreover, in Web Service it is regular case that we are exchanging data more than 117 bytes with those bulky xml tags. We can surly use other mechanism such as compression, use other medium such as JSON instead of XML to reduce the data size, but none of these ensures that the data size will be always less&nbsp; than 117 byte. Here comes the Symmetric Key into the scene. Let me revise the previous proof of concept.</p>
<h3>Revised Solution</h3>
<p>
<table style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; BORDER-LEFT: #000000 1px solid; BORDER-BOTTOM: #000000 1px solid" cellSpacing=0 cellPadding=3 border=0>
    <tbody>
        <tr>
            <th style="COLOR: #ffffff; HEIGHT: 22px; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left"></th>
            <th style="WIDTH: 33%; COLOR: #ffffff; HEIGHT: 22px; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Client</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; HEIGHT: 22px; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Server</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; HEIGHT: 22px; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Hacker</th>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>1.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client creates random Public/Private Key Pair and sends the Server the Public Part.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server in turns creates a random Public/Private Key Pair and returns Public Part to client.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Hacker can only view both parties Public Key.<br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>2.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client Creates a random symmetric key, encrypts it with the previously received Server Public Key, and sends it to Server.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server receives the client symmetric key decrypts it with the previously generated Server Private Key.<br>&nbsp;<br><br>Now the Server Creates a Random Symmetric Key encrypts it with the Client Public Key and returns it back to the Client.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Same thing happens for the Hacker as in the initial proof of concept. The Hacker cannot decrypt the data, as it requires the Server Private key.<br>&nbsp;<br>Again, the Hacker only has the Client Public Key and thus it is not possible for the Hacker to decrypt the Server Symmetric Key.<br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>3.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client Receives the encrypted Symmetric Key of Server. It decrypts it with previously generated Client Private Key.<br><br>Client Encrypts the target data with the previously generated Client Symmetric key and sends to Server.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br><br><br><br><br>Server Receives the Encrypted data, Server decrypts it with the previously received Client Symmetric key.<br><br>Server Process the Data.<br><br>Server encrypts the process result with the previously generated Server Symmetric Key and returns back to Client.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br><br><br><br><br>The Hacker does not have the Client Symmetric Key and so it is not possible to decrypt the data.<br>&nbsp;<br>&nbsp;<br>&nbsp;<br>Again, the Hacker does not have the Server Symmetric Key and thus it is not possible for the hacker to decrypt the process result.<br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>4.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Client receives the encrypted process result and decrypts it with the previously received Server Symmetric Key.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br><br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: right"><br>5.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Client returns to regular processing.<br>&nbsp;</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>Server goes to sleep.<br>&nbsp;</td>
            <td style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Dumb Hacker cries and but the intelligent hacker might be thinking, &#8220;Hey there is something to hack&#8221;.<br>&nbsp;</td>
        </tr>
    </tbody>
</table>
<br>In this revised version an extra step just been introduced -the step 2, where both the parties exchanging Symmetric key (Lets call it session key from now on) instead of the actual data like in the initial concept. Now you might be thinking does not the RSA length limitation applies when encrypting the session key. The Answer is no, it does not, even we are exchange the most secure Rijndael key(256 bit) , the data length becomes 44 byte (256/8) which we can easily encrypted/decrypted with RSA. The only weakness of the above solution is, if the intelligent hacker can somehow discover the session key of the both parties, but which I assume is very difficult to discover based upon the encrypted data. <br><br></p>
<h3>Where is the Digital Signature?</h3>
<p>You might be thinking this article starts with Digital Signature, but still no flavor of it. Let me tell you, although the digital signature is not required but you can implement it to make the solution more secure and ensure that even the intelligent hacker can revive both the parties session key but there will be no way the hacker can tamper the data. Here is the final solution.<br><br></p>
<h3>The Final Solution</h3>
<p>
<table style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; BORDER-LEFT: #000000 1px solid; BORDER-BOTTOM: #000000 1px solid" cellSpacing=0 cellPadding=3 border=0>
    <tbody>
        <tr>
            <th style="WIDTH: 21px; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left"></th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Client</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Server</th>
            <th style="WIDTH: 33%; COLOR: #ffffff; BACKGROUND-COLOR: #000000; TEXT-ALIGN: left">The Hacker</th>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; WIDTH: 21px; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>1.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client creates random Public/Private Key Pair and sends the Server the Public Part.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server in turns creates a random Public/Private Key Pair and returns Public Part to client.<br></td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Hacker can only view both parties Public Key.<br></td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; WIDTH: 21px; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>2.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client Creates a random Session key, encrypts it with the previously received Server Public Key, Digitally Signs it with the Client Private Key and sends it to Server.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Server receives the client session key, Checks it integrity with the Client Public Key, if the Key has not been tampered, it decrypts the Client Session Key with the previously generated Server Private Key.<br><br>Now the Server Creates a Random Session Key encrypts it with the Client Public Key, Digitally Signs it with the Server Private Key and returns it back to the Client.<br></td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Same thing happens for the Hacker as in the previous two solutions. The Hacker cannot decrypt the data, as it requires the Server Private key, moreover the Hacker founds the data is now Digitally Signed.<br><br>Again, the Hacker only has the Client Public Key and thus it is not possible for the Hacker to decrypt the Server Session.<br></td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; WIDTH: 21px; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>3.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>Client Receives the encrypted Session Key of Server. It verifies it with the Server Public Key, if not tampered, it decrypts it with previously generated Client Private Key.<br><br>Client Encrypts the target data with the previously generated Client Session Key, Digitally Signs it and sends to Server.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br><br><br><br>Server Receives the Encrypted data, Server verifies it with Client Public Key, if not tampered it decrypts the data with the previously received Client Session Key.<br><br>Server Process the Data.<br><br>Server encrypts the process result with the previously generated Server Session Key, Digitally Signs it with the Server Private Key and returns it back to Client<br></td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br><br><br><br><br>The Hacker does not have the Client Session Key and thus it is not possible to decrypt the data, also founds the data is Digitally Signed.<br><br><br><br>Again, the Hacker does not have the Server Session Key and so it is not possible for the hacker to decrypt the process result.<br></td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; WIDTH: 21px; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: right"><br>4.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>The Client receives the encrypted process result, verifies it with Server Public Key and decrypts it with the previously received Server Session Key.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br></td>
            <td style="VERTICAL-ALIGN: top; BORDER-BOTTOM: #000000 1px solid; TEXT-ALIGN: left"><br>&nbsp;</td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; WIDTH: 21px; TEXT-ALIGN: right"><br>5.</td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>The Client returns to regular processing.<br></td>
            <td style="BORDER-RIGHT: #000000 1px solid; VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>Server goes to sleep.</td>
            <td style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><br>Both the Hacker cries now.<br></td>
        </tr>
    </tbody>
</table>
<br>In the final solution, the digital signature has been introduced to ensure the data has not been tampered between the communications.<br></p>
<h2>The Source Code</h2>
<p>The attached source contains the implementation of the final solutions, RSA&#8217;s limitation of large data encryption, a bit performance benchmarking and few numbers of handy classes which you can use in your own solution. </p>
<h2>Disadvantages</h2>
<ul>
    <li>The Solution is not 100% secure. But its is pretty good to use in production environment.
    <li>Much more coding, as you will not get the benefit of Strongly Typed Objects that generated web service proxy provides. You have to write some kind of wrapper (Like the ServiceWrapper.cs in the Client) to get the benefit of Strongly Typed Object.
    <li>The Service will not be stateless, as it needs to use session, cache etc to identify the client.
    <li>Performance will not be optimal, as it requires two more calls to initialize the secure communication also the encryption/decryption/verification has extra overhead. </li>
</ul>
<!-- Article Ends -->
<script src="/script/togglePre.js" type=text/javascript></script>
<h2>About kazimanzurrashid</h2>
<div style="OVERFLOW: hidden">
<table border=0>
    <tbody>
        <tr vAlign=top>
            <td class=smallText noWrap><br></td>
            <td class=smallText>Kazi Manzur Rashid is a die-hard fan of Microsoft Technology. He has delivered many diversified solution in his professional career which includes SpywareKill - first ever anti-Spyware tool in Microsoft .NET technology, PrepaidATM the largest ATM Card network currently acquired by GlobalATM, LicenseLeader - Licensing Framework for .NET Components and many more. He is currently working in Pageflakes Ltd - A Leading Player in Personalized Web Desktop. His former role includes the Tech Lead of Velocity Global Resource offshore development team.<br><br>He lives in Dhaka, Bangladesh.<br><br>You can reach him here: <br>Email: KaziManzurRashid at GMail dot com<br>Blog: <a href="http://geekswithblogs.net/rashid/" target=_blank><u><font color=#0000ff>http://geekswithblogs.net/rashid/</font></u></a></td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28362.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:15 <a href="http://www.cnitblog.com/MartinYao/articles/28362.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Switching Between HTTP and HTTPS Automatically</title><link>http://www.cnitblog.com/MartinYao/articles/28361.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:10:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28361.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28361.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28361.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28361.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2/WebPageSecurity_v2_Src_v31.zip"><u><font color=#0000ff>Download source files (version 3.1 for .NET 2.0) - 33.9 KB</font></u></a>
    <li><a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2/WebPageSecurity_v2_Demo_v31.zip"><u><font color=#0000ff>Download demo project (version 3.1 for .NET 2.0) - 8.33 KB</font></u></a>
    <li><a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2/WebPageSecurity_v2_Src_v26.zip"><u><font color=#0000ff>Untested source files (version 2.6 for .NET 1.1) - 32.7 KB</font></u></a>
    <li><a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2/WebPageSecurity_v2_Binaries.zip"><u><font color=#0000ff>Download binaries (all versions) - 13.3 KB</font></u></a> </li>
</ul>
<p><img height=665 src="http://www.codeproject.com/aspnet/WebPageSecurity_v2/WebPageSecurity_v2.gif" width=524></p>
<h2>Introduction</h2>
<p>Let's face it. Providing sensitive information on a web site is a risk. Many visitors will not give out that kind of data even if they see that the web site claims security. Many more will certainly not reveal their personal details if the warm-and-fuzzy closed padlock isn't visible in their browser window.</p>
<p>See the <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>What's new</font></u></a> section for the latest updates.</p>
<h2>Background</h2>
<p>Enter Secure Sockets Layer. SSL is a developer's tool for securing the transmission of data. Whether you are encrypting pages for the checkout area of an e-commerce site or you are protecting the personal statistics that your users supply you for marketing; SSL is ideal. A trusted certificate installed on the web server offers visitors that good feeling of a secure environment.</p>
<p>There are caveats when implementing a web site that makes use of the HTTPS protocol. I'm not referring to the technical nuances you, or a system administrator, must face when installing a certificate on the server. What about simply adding a link from one page to another page that should be secured?</p>
<p>Those of you who have experience with writing web pages that use SSL probably know where I'm going with this. You cannot switch protocols unless you provide an absolute URL. Therefore, in order to allow a visitor to click on a link that should take them to a secure web page, the reference must be absolute.</p>
<pre lang=text>https://www.codeproject.com/secure/getsensitiveinfo.asp</pre>
<p>To make things worse, many browsers download pages referenced by a relative URL with the same protocol as the last request. So, if you had a link in the above file to another page in the root directory, that you wanted to show with the HTTP protocol, it would also have to be absolute.</p>
<pre lang=html>&lt;!--
The following will actually be translated as
https://www.codeproject.com/welcome.asp;
thus, retaining the HTTPS protocol that was last used.
--&gt;
&lt;a href="../welcome.asp"&gt;Back to the Welcome Page.&lt;/a&gt;</pre>
<p>Generally, it is <strong>not</strong> a good idea to encrypt every single page request with SSL. It makes for slower page serves and more bandwidth usage. It is also more intensive on the server's CPU; something your hosting provider may not be pleased with.</p>
<h2>A Solution</h2>
<p>Being forced to use absolute URLs for internal links in a web site is less than appealing. The next thing you know, the web site's domain name changes (for any number of reasons) or you have a staging server, which means you have to maintain a separate copy of the site for that set of absolute URLs. It makes much more sense to mark certain files and/or entire directories as "secure". This would allow you the benefit of using relative URLs freely within your web pages. If an existing page needs to be made secure, you simply add it to the list of marked files instead of finding and replacing all links to the page with an absolute URL.</p>
<p>That's where <code>SecureWebPageModule</code> comes in. <code>SecureWebPageModule</code> is a class that implements the <code>IHttpModule</code> interface. HTTP modules give programmers a means of "attaching" to a web application to process its events. It's like descending from the <code>System.Web.HttpApplication</code> class and overriding the application and session events in a <em>Global.asax</em> file. The main difference is you don't have to worry about copying and pasting the same code into the file for every application that is to use it. HTTP modules are simply "linked in" to a web application and become part of the pipeline.</p>
<p>The goal of this security solution is to allow a developer to easily secure a web site without the need to hard-code absolute URLs. This is accomplished by listing the files and/or directories that should be secured by SSL. It only seems natural to have a custom configuration section for this.</p>
<h2>Configuration</h2>
<pre lang=xml>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;configuration&gt;
...
&lt;secureWebPages
mode="RemoteOnly"
encryptedUri="secure.mysite.com"
unencryptedUri="www.mysite.com"
maintainPath="True"
warningBypassMode="AlwaysBypass"
bypassQueryParamName="BypassSecurityWarning"
ignoreHandlers="WithStandardExtensions"&gt;
...
&lt;/secureWebPages&gt;
...
&lt;system.web&gt;
...
&lt;/system.web&gt;
&lt;/configuration&gt;</pre>
<table cellSpacing=1 cellPadding=3 width="60%" bgColor=#000000 border=0>
    <tbody>
        <tr bgColor=#fbedbb>
            <th width="50%">Attribute and possible values</th>
            <th width="50%">Description</th>
        </tr>
        <tr bgColor=white>
            <td vAlign=top width="50%"><code>mode</code>
            <table borderColor=#fbedbb cellSpacing=0 cellPadding=3 border=1>
                <tbody>
                    <tr>
                        <td vAlign=top><code>On</code> (Default)</td>
                        <td vAlign=top>Security is enabled and all requests are monitored.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>RemoteOnly</code></td>
                        <td vAlign=top>Only requests from remote clients are monitored.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>LocalOnly</code></td>
                        <td vAlign=top>Only requests from the local server are monitored.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>Off</code></td>
                        <td vAlign=top>No requests are monitored.</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td vAlign=top width="50%">The mode determines when the module will monitor requests for redirection.</td>
        </tr>
        <tr bgColor=#fbedbb>
            <td vAlign=top width="50%"><code>encryptedUri</code></td>
            <td vAlign=top width="50%">Set to a specific URI to indicate where to redirect when the module decides that security is needed.</td>
        </tr>
        <tr bgColor=white>
            <td vAlign=top width="50%"><code>unencryptedUri</code></td>
            <td vAlign=top width="50%">Set to a specific URI to indicate where to redirect when the module decides that security is <strong>not</strong> needed.</td>
        </tr>
        <tr bgColor=#fbedbb>
            <td vAlign=top width="50%"><code>maintainPath</code>
            <table borderColor=white cellSpacing=0 cellPadding=3 border=1>
                <tbody>
                    <tr>
                        <td vAlign=top><code lang=vbnet><span class=vb-keyword>True</span></code> (Default)</td>
                        <td vAlign=top>When redirecting to the specified URIs (<code>encryptedUri</code> and <code>unencryptedUri</code>), the current path is maintained.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code lang=vbnet><span class=vb-keyword>False</span></code></td>
                        <td vAlign=top>Prevents the module from maintaining the current path when redirecting to the specified URIs (<code>encryptedUri</code> and <code>unencryptedUri</code>).</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td vAlign=top width="50%">This flag indicates whether or not the module should maintain the current path when redirecting to <code>encryptedUri</code> or <code>unencryptedUri</code>.</td>
        </tr>
        <tr bgColor=white>
            <td vAlign=top width="50%"><code>warningBypassMode</code>
            <table borderColor=#fbedbb cellSpacing=0 cellPadding=3 border=1>
                <tbody>
                    <tr>
                        <td vAlign=top><code>AlwaysBypass</code></td>
                        <td vAlign=top>Always bypass security warnings when switching to an unencrypted page.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>BypassWithQueryParam</code> (Default)</td>
                        <td vAlign=top>Only bypass security warnings when switching to an unencrypted page if the proper query parameter is present.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>NeverBypass</code></td>
                        <td vAlign=top>Never bypass security warnings when switching to an unencrypted page.</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td vAlign=top width="50%">Use this attribute to bypass any security warnings that may be displayed.</td>
        </tr>
        <tr bgColor=#fbedbb>
            <td vAlign=top width="50%"><code>bypassQueryParamName</code></td>
            <td vAlign=top width="50%">Set to the name of a query parameter that will indicate to the module to bypass any security warning if <code>warningBypassMode</code> = "<code>BypassWithQueryParam</code>". The default value is "<code>BypassSecurityWarning</code>".</td>
        </tr>
        <tr bgColor=white>
            <td vAlign=top width="50%"><code>ignoreHandlers</code>
            <table borderColor=#fbedbb cellSpacing=0 cellPadding=3 border=1>
                <tbody>
                    <tr>
                        <td vAlign=top><code>BuiltIn</code> (Default)</td>
                        <td vAlign=top>The built-in HTTP handlers should be ignored. Currently, these are <em>Trace.axd</em> and <em>WebResource.axd</em>.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>WithStandardExtensions</code></td>
                        <td vAlign=top>All files that have an extension that corresponds to standard HTTP handlers should be ignored. Currently, that is <em>.axd</em> files.</td>
                    </tr>
                    <tr>
                        <td vAlign=top><code>None</code></td>
                        <td vAlign=top>No HTTP handlers should be ignored unless specifically specified in the files or directories entries.</td>
                    </tr>
                </tbody>
            </table>
            </td>
            <td vAlign=top width="50%">This attribute is used to instruct the module to ignore HTTP handlers. This is only used in version 3.x and above.</td>
        </tr>
    </tbody>
</table>
<p><code>mode</code> behaves like the <code>customErrors mode</code> attribute. Use it to avoid redirecting to the HTTPS protocol on your development or production machine when you don't have a secure certificate installed.</p>
<p>The <code>encryptedUri</code> and <code>unencryptedUri</code> attributes are perfect for those situations where your secure certificate is associated with a different site than the one needing security. I know; examples are better than just explaining it. Okay then, visit <a href="http://www.searsmastercard.com/" target=_blank><u><font color=#0000ff>searsmastercard</font></u></a> and notice the security alert that appears. If you are using IE, you should see a security alert window appear, warning you that something is wrong with the site's certificate. Everything is okay except the name doesn't match the name of the site. Click on the View Certificate button and note the site listed next to "Issued to". www.searscard.com is the site they purchased the certificate for. What does this mean? Well, the site should be www.searscard.com; however, the IT folks at Sears thought it would be a good idea to purchase the searsmastercard.com domain as well to allow for another point of entry. Both DNS records point to the same place on their web server. They have a few options that would prevent this alert from displaying to their users, but two are the most obvious. They can redirect to https://www.searscard.com when users visit the default page on www.searsmastercard.com, or they could upgrade their site to ASP.NET and download this module ;-). All they'd have to do then, is set the <code>encryptedUri</code> attribute to www.searscard.com and all would be wonderful. Likewise, <code>unencryptedUri</code> may be specified to send the user back to another domain or specific URI when the module removes security. An example would be to redirect secure requests to secure.mysite.com, and requests that don't need to be secure could be redirected back to www.mysite.com. <code>maintainPath</code> is used in conjunction with the aforementioned attributes. When the module redirects to the <code>encryptedUri</code> or <code>unencryptedUri</code>, it appends the current path before sending the user on their way. You can turn off this behavior by setting <code>maintainPath</code> to "<code lang=vbnet><span class=vb-keyword>False</span></code>".</p>
<p>In certain circumstances, IE displays the message, "You are about to be redirected to a connection that is not secure". This only happens as a result of a "double redirect" to an unsecured page. That is, when a page is requested via HTTPS, the programmer's code performs a relative redirect, then the module performs an absolute redirect via the HTTP protocol. Use the <code>warningBypassMode</code> attribute to bypass the security warning as desired. If you choose to bypass security warnings via the default <code>BypassWithQueryParam</code> option, you may specify the name of the query parameter with the <code>bypassQueryParamName</code> attribute. Use this when you only want to run the bypass code on the rare occasions you know the security warning will appear, and request the page with the query parameter specified. An example of this is when your code will be posting back to a secure page for server-side processing and then redirecting to a page that the module will deem unsecured. Simply redirect to the page and append the <code>bypassQueryParamName</code> with any value like so: <code lang=vbscript>Response.Redirect(<span class=vb-string>"MyUnsecurePage.aspx?BypassSecurityWarning=True"</span>);</code>.</p>
<p>I received a couple of suggestions on how to solve the above warning. There was one suggestion that involved a configuration attribute that would point the module to a "redirector page". This page would be sent a parameter containing the page that should be redirected to, and it would change the location via meta refresh and JavaScript as a backup. The idea is a good one. I just don't like making the user of this module create a page that has preset code in it. Therefore, if the module determines that it should bypass the warning, it will render the necessary page itself, complete with meta tag and JavaScript. This will cause a client-side redirect and avoid the security warning.</p>
<p>One power of ASP.NET is the ability to create custom HTTP handlers that act similar to this module. The handlers are invoked when a certain file, or type of file, is requested from the server. In ASP.NET 2.0, embedded resources make heavy use of the <em>WebResource.axd</em> virtual file to dynamically serve images and JavaScript that don't actually have a physical file. When used, these handlers may cause mixed security warnings unless the module is instructed to ignore them. The <code>ignoreHandlers</code> attribute lets you generally ignore these handlers quite easily. You may configure the module to ignore any standard HTTP handler with a file extension of <em>.axd</em> by setting the attribute to <code>WithStandardExtensions</code>. The default setting is <code>BuiltIn</code> and forces the module to just ignore the two built-in handlers; <em>Trace.axd</em> and <em>WebResource.axd</em>.</p>
<p>Now...on to <code>file</code> and <code>directory</code> entries.</p>
<h4>secureWebPages for .NET 1.1</h4>
<pre lang=xml>    ...
&lt;secureWebPages&gt;
&lt;file path="Default.aspx" secure="Insecure" /&gt;
&lt;file path="Admin/MoreAdminStuff.aspx" secure="Insecure" /&gt;
&lt;file path="Legal/Copyright.aspx" secure="Ignore" /&gt;
&lt;file path="Lib/PopupCalendar.aspx" secure="Ignore" /&gt;
&lt;directory path="/" recurse="False" /&gt;
&lt;directory path="Admin" /&gt;
&lt;directory path="Admin/Info" secure="Insecure" /&gt;
&lt;directory path="Members/Secure" recurse="True" /&gt;
&lt;/secureWebPages&gt;
...</pre>
<h4>secureWebPages for .NET 2.0</h4>
<pre lang=xml>    ...
&lt;secureWebPages&gt;
&lt;files&gt;
&lt;add path="Default.aspx" secure="Insecure" /&gt;
&lt;add path="Admin/MoreAdminStuff.aspx" secure="Ignore" /&gt;
&lt;add path="Legal/Copyright.aspx" secure="Ignore" /&gt;
&lt;add path="Lib/PopupCalendar.aspx" secure="Ignore" /&gt;
&lt;/files&gt;
&lt;directories&gt;
&lt;add path="/" recurse="False" /&gt;
&lt;add path="Admin" /&gt;
&lt;add path="Admin/Info" secure="Insecure" /&gt;
&lt;add path="Members/Secure" recurse="True" /&gt;
&lt;/directories&gt;
&lt;/secureWebPages&gt;
...</pre>
<p>Notice that you can now include the application root as a <code>directory</code> entry. There is no longer an <code>ignore</code> attribute for each entry. It has been replaced by the <code>secure</code> attribute. This attribute tells the module how to handle that particular file or directory. The default value is <code lang=vbnet>Secure</code>, which simply means the module should redirect to the HTTPS protocol when that file, or a file in that directory, is requested. Setting the attribute to <code lang=vbnet>Insecure</code> will force a matching request to be served <strong>without</strong> SSL. In the example above, although the application root is secured, the <em>Default.aspx</em> page in the application root should not be. The <code>secure</code> attribute may also have a value of <code>Ignore</code> which mimics the functionality of version 1's <code>ignore</code> attribute. Any request to a file with a matching <code>file</code> or <code>directory</code> entry marked with <code>secure=<span class=cpp-string>"Ignore"</span></code> will be ignored by the module. This is good when a page should remain in the same protocol as the last request, such as pop-up windows used by both secure and unsecured pages. Another example of this is an <em>.aspx</em> page that is used to serve content other than HTML and is referenced from within a secure page. There are times when an <em>.aspx</em> page will serve an image or a style sheet and is included by a secure page with the appropriate <code lang=html>&lt;img&gt;</code> or <code lang=html>&lt;link&gt;</code> tag. In these cases, a visitor of such a page would receive a warning that there is a mixture of secure and unsecured items. That usually doesn't make users feel too good about a page. Just include an entry for the file and set <code>secure</code> to <code>Ignore</code>, and there will be no problems.</p>
<p>You may also provide the <code>recurse</code> attribute for <code>directory</code> entries. Setting this attribute to <code lang=vbnet><span class=vb-keyword>True</span></code> will inform the module to include all files in any sub-directories when monitoring requests.</p>
<h2>Adding the Module to Applications</h2>
<p>There are two options for adding the module to your applications. The first is to add the module to an individual application. This requires that you edit the <em>web.config</em> file of the application. You will need to add a custom configuration section handler for the <code>&lt;secureWebPages&gt;</code> section and a module addition to the <code>&lt;httpModules&gt;</code> section.</p>
<h4>configSections for .NET 1.1</h4>
<pre lang=xml>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;configuration&gt;
...
&lt;configSections&gt;
...
&lt;section
name="secureWebPages"
type="Ventaur.Web.Security.SecureWebPageSectionHandler,
WebPageSecurity"
allowLocation="false" /&gt;
&lt;/configSections&gt;
...
&lt;/configuration&gt;</pre>
<h4>configSections for .NET 2.0</h4>
<pre lang=xml>&lt;?xml version="1.0"?&gt;
&lt;configuration&gt;
...
&lt;configSections&gt;
...
&lt;section
name="secureWebPages"
type=
"Ventaur.Web.Security.Configuration.SecureWebPageSettings,
WebPageSecurity" /&gt;
&lt;/configSections&gt;
...
&lt;/configuration&gt;</pre>
<h4>httpModules for .NET 1.1 and 2.0</h4>
<pre lang=xml>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;system.web&gt;
...
&lt;httpModules&gt;
...
&lt;add
name="WebPageSecurity"
type="Ventaur.Web.Security.SecureWebPageModule,
WebPageSecurity" /&gt;
&lt;/httpModules&gt;
...
&lt;/system.web&gt;
...
&lt;/configuration&gt;</pre>
<p>The second option is to add the module to <strong>all</strong> web applications on the server. You will need to make similar modifications to the <em>machine.config</em> file. Editing the <em>machine.config</em> file should only be performed by a knowledgeable person with "Administrator" privileges. <em>Always make a backup of your machine.config file before editing it</em>. If you choose to add the module and configuration section handlers to your <em>machine.config</em> file, you should sign the assembly with a strong name and register it in the Global Assembly Cache (GAC). The "<em>AssemblyInfo.cs</em>" file provided with the project source should have a line near the bottom that is commented to prevent signing the assembly. To sign the assembly during a compile, un-comment this line:</p>
<pre lang=cs>[assembly: AssemblyKeyFile(<span class=cpp-string>"..\\..\\..\\Key.snk"</span>)]</pre>
<p>For more information on registering an assembly in the GAC, please refer to the .NET Framework documentation.</p>
<h2>Notes</h2>
<p>Please, be aware that although IIS allows you to "Require a secure channel (SSL)" for a folder's "Directory Security", this module will not work properly if you do so. IIS will intercept the request before passing it along to the module and reject insecure connections. Therefore, if you want to use this module, do not require SSL from IIS.</p>
<p>Also, testing this module on a development machine without an installed SSL certificate will yield unexpected results. The browser may appear to "hang" or fail altogether. This is because it is being sent to a page that should be encrypted, but is not.</p>
<h2>Version History</h2>
<ul>
    <li>Version 3.1
    <ul>
        <li>See <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>what's new</font></u></a> for details.</li>
    </ul>
    <li>Version 2.6
    <ul>
        <li>See <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>what's new</font></u></a> for details.</li>
    </ul>
    <li>Version 3.0
    <ul>
        <li>See <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>what's new</font></u></a> for details.</li>
    </ul>
    <li>Version 2.5
    <ul>
        <li>Bug fix: encryptedUri/unencryptedUri domain differing from the current request domain. For example, if unencryptedUri="www.mysite.com", and the user visits a page via mysite.com, 4 characters ("www.") were dropped when the path was maintained during a redirect. This bug was squashed.
        <li>See <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>what's new</font></u></a> for more details.</li>
    </ul>
    <li>Version 2.1
    <ul>
        <li>Changed the <code>maintainApplicationPath</code> attribute to <code>maintainPath</code>.
        <li>Improved the code that redirects to another host via the <code>encryptedUri</code> and <code>unencryptedUri</code>. </li>
    </ul>
    <li>Version 2.0
    <ul>
        <li>Current version: See <a href="http://www.codeproject.com/aspnet/WebPageSecurity_v2.asp#whatsnew"><u><font color=#0000ff>what's new</font></u></a> for details. </li>
    </ul>
    <li>Version 1.0.1
    <ul>
        <li>Added code to skip comments in the configuration file. Thanks goes to dcbrowser.
        <li>Minor performance changes to select areas of the code. </li>
    </ul>
    <li>Version 1.0.0
    <ul>
        <li>Initial release. </li>
    </ul>
    </li>
</ul>
<h2><a name=whatsnew>What's New</a></h2>
<h4>Version 3.1</h4>
<ul>
    <li>Added the <code>ignoreHandlers</code> attribute to generally ignore HTTP handlers. I normally do <strong>not</strong> like setting the default of a new setting to something that is different from the previous version's default functionality, but I believe it is acceptable in this case. The default setting is <code>BuiltIn</code> to force the module to ignore requests for <em>Trace.axd</em> and <em>WebResource.axd</em>.
    <li>The module now accounts for cookie-less sessions when redirecting without a specified <code>encryptedUri</code> or <code>unencryptedUri</code>.
    <li>A tiny change was made to the <code>RequestEvaluator.Evaluate</code> method that allows you to force an evaluation, despite the <code>mode</code> setting in the configuration. This should allow for better testing and simulation. </li>
</ul>
<h4>Version 3.0</h4>
<ul>
    <li>Ported to .NET 2.0.
    <li>Configuration re-written to conform to the new configuration API. </li>
</ul>
<h4>Version 2.6</h4>
<ul>
    <li>The module now accounts for cookie-less sessions when redirecting without a specified <code>encryptedUri</code> or <code>unencryptedUri</code>.
    <li>A tiny change was made to the <code>RequestEvaluator.Evaluate</code> method that allows you to force an evaluation, despite the <code>mode</code> setting in the configuration. This should allow for better testing and simulation. </li>
</ul>
<h4>Version 2.5</h4>
<ul>
    <li>Separated appropriate logic into static helper classes. This adds benefit for testing purposes. Now, simple testing can be achieved by simulating the processes.
    <li>Removed the configuration restriction of "order matters" for directory elements. Now, directory elements may be included in the configuration in any order. The module processes directory matches with the attitude that the deepest match is the best match. In other words, if a request is made for a page located in Admin/Reports/ and the configuration file has a directory element for both "Admin/" and "Admin/Reports/", it no longer matters if "Admin/" is first. Before, the module would stop once it found a match, despite recursion settings. Now, it finds the most accurate (deepest) match. Thanks goes out to balazs_hideghety for getting me started with this feature update. </li>
</ul>
<h4>Version 2.0 and 2.1</h4>
<ul>
    <li>Source code provided in C# <strong>and</strong> VB.NET.
    <li>The configuration settings loader is now called once in an application <code>Init</code> event handler, and if the settings indicate to not use security, a handler for the <code>BeginRequest</code> event is not added. This improves the overall performance of the module and any application including it. Thanks goes to Diego.
    <li>The root directory of the virtual application can now be secured by including a <code>directory</code> tag with a path of "/". Thanks to Andy for pointing this one out.
    <li>A <code>mode</code> attribute was added to the <code>secureWebPages</code> section. Possible values are "<code>On</code>", "<code>RemoteOnly</code>", "<code>LocalOnly</code>" or "<code>Off</code>". Thanks to Andy for this suggestion.
    <li>The <code>encryptedUri</code> and <code>unencryptedUri</code> attributes were added to the <code>secureWebPages</code> section. Set to a specific URI to indicate where the module should redirect when it decides whether security is needed or not. Thanks to Andy for this suggestion.
    <li>The <code>maintainPath</code> attribute was added to the <code>secureWebPages</code> section. A value of "<code lang=vbnet><span class=vb-keyword>False</span></code>" will prevent the module from maintaining the current path when redirecting to any specified URIs.
    <li>The <code>warningBypassMode</code> attribute was added to the <code>secureWebPages</code> section. Possible values are "<code>AlwaysBypass</code>", "<code>NeverBypass</code>" or "<code>BypassWithQueryParam</code>".
    <li>The <code>bypassQueryParamName</code> attribute was added to the <code>secureWebPages</code> section. Set the value to the name of a query string parameter that indicates to the module to bypass any security warning if <code>warningBypassMode</code> is set to "<code>BypassWithQueryParam</code>".
    <li>The <code>ignore</code> attribute was removed from the <code>file</code> and <code>directory</code> tags.
    <li>The <code>secure</code> attribute was added to the <code>file</code> and <code>directory</code> tags. Possible values are "<code>True</code>", "<code>False</code>" or "<code>Ignore</code>".
    <li><code>directory</code> tags may include the new <code>recurse</code> attribute. If "<code lang=vbnet><span class=vb-keyword>True</span></code>", all files in any sub-directories are included. </li>
</ul>
<!-- Article Ends -->
<script src="/script/togglePre.js" type=text/javascript></script>
<h2>About Matt Sollars</h2>
<div style="OVERFLOW: hidden">
<table border=0>
    <tbody>
        <tr vAlign=top>
            <td class=smallText noWrap><br></td>
            <td class=smallText>I began programming on my Commodore 64 at around the age of 12. After migrating to DOS and then Windows, I decided to take on the Web. Several languages and platforms later, I have settled in with .NET nicely. I am currently the lead developer for a small company that offers its services to high-end clients.<br><br>The love of a finished application is usually at war with the desire to improve it as soon as it's released (they're never really finished).</td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28361.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:10 <a href="http://www.cnitblog.com/MartinYao/articles/28361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>保护 ASP.NET 应用程序使用客户端证书</title><link>http://www.cnitblog.com/MartinYao/articles/28360.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:06:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28360.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28360.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28360.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28360.html</trackback:ping><description><![CDATA[<p>&nbsp;保护 ASP.NET 应用程序使用客户端证书</p>
<div class=appliesToLink><a href="http://support.microsoft.com/kb/315588#appliesto"></a>&nbsp;</div>
<script>function loadTOCNode(){}</script>
<div class="disclaimer mt">
<div class=label>注意：这篇文章是由无人工介入的自动的机器翻译系统翻译完成。这些文章是微软为不懂英语的用户提供的, 以使他们能够理解这些文章的内容。微软不保证机器翻译的正确度，也不对由于内容的误译或者客户对它的使用所引起的任何直接的, 或间接的可能的问题负责。</div>
</div>
<div class=articleProperty>
<table>
    <tbody>
        <tr>
            <td>文章编号</td>
            <td>:</td>
            <td>315588</td>
        </tr>
        <tr>
            <td>最后修改</td>
            <td>:</td>
            <td>2005年7月8日</td>
        </tr>
        <tr>
            <td>修订</td>
            <td>:</td>
            <td>4.1</td>
        </tr>
    </tbody>
</table>
</div>
<div class=toc id=tocDiv style="DISPLAY: block">
<h5>本页</h5>
<div depth="1"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>概要</span></a>
<div class=tocLine>
<div depth="2"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要求</span></a>
<div class=tocLine></div>
</div>
<div depth="2"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>创建 ASP.NETWeb 应用程序</span></a>
<div class=tocLine></div>
</div>
<div depth="2"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>为基于证书的验证配置 Web 服务器</span></a>
<div class=tocLine>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要创建服务器端证书申请</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>以提交服务器端证书请求</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要颁发服务器端证书</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要安装服务器端证书</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要为 SSL 和客户端证书配置 SecureSite 站点</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要验证基于证书的验证</span></a>
<div class=tocLine></div>
</div>
</div>
</div>
<div depth="2"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>安装客户证书</span></a>
<div class=tocLine>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要申请客户端证书</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要颁发客户端证书</span></a>
<div class=tocLine></div>
</div>
<div depth="3"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>要安装客户端证书</span></a>
<div class=tocLine></div>
</div>
</div>
</div>
<div depth="2"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>验证它工作</span></a>
<div class=tocLine></div>
</div>
</div>
</div>
<div depth="1"><a onclick="return tocScrollTo(this);" href="http://support.microsoft.com/kb/315588#"><img alt="" src="http://support.microsoft.com/library/images/support/kbgraphics/public/en-us/downarrow.gif"><span class=tocTxt>参考</span></a>
<div class=tocLine></div>
</div>
</div>
<script type=text/javascript>
var sectionFilter = "type != 'notice' && type != 'securedata' && type != 'querywords'";
var tocArrow = "/library/images/support/kbgraphics/public/en-us/downarrow.gif";
var depthLimit = 10;
var depth3Limit = 10;
var depth4Limit = 5;
var depth5Limit = 3;
var tocEntryMinimum = 1;
</script>
<script src="/common/script/gsfx/kbtoc.js?8" type=text/javascript></script>
<noscript></noscript>
<div class=section>
<h2 class=subTitle id=tocHeadRef>概要</h2>
<script type=text/javascript>loadTOCNode(1, 'summary');</script>
<div class=sbody>对于高度安全 Web 应用程序, 如 Internet 银行站点, 可能需要实现对用户身份验证更安全解决方案比用户名和密码组合。 可以使用客户端数字证书来验证对用户身份 此外, 可以客户端数字证书映射到 Windows 帐户必要在服务器上。<br><br><br>
<p class=topOfPage><a href="http://support.microsoft.com/kb/315588#top"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></p>
<h3 id=tocHeadRef><span><a id=2></a></span>要求</h3>
<script type=text/javascript>loadTOCNode(2, 'summary');</script>
以下列表概括了推荐硬件、 软件、 网络结构, 以及 ServicePack， 您需要：
<table class="list ul">
    <tbody>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>Microsoft Windows 2000 服务 pack 2 服务器</td>
        </tr>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>Microsoft Internet Explorer 6.0</td>
        </tr>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>MicrosoftVisualStudio.NET</td>
        </tr>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>Microsoft 证书服务 （如果必须生成自己的证书）</td>
        </tr>
    </tbody>
</table>
本文假定您已熟悉以下主题：
<table class="list ul">
    <tbody>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>使用 VisualBasic 或 VisualC # ASP.NET 开发</td>
        </tr>
        <tr>
            <td class=bullet>&#8226;</td>
            <td class=text>MicrosoftInternet 信息服务 (IIS) 配置</td>
        </tr>
    </tbody>
</table>
<br>
<p class=topOfPage><a href="http://support.microsoft.com/kb/315588#top"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></p>
<h3 id=tocHeadRef><span><a id=3></a></span>创建 ASP.NETWeb 应用程序</h3>
<script type=text/javascript>loadTOCNode(2, 'summary');</script>
本节, 中创建简单的 ASP.NET 应用程序。 要遵循, 节中您将使用客户端证书身份验证来保护应用程序。
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>启动 VisualStudio.NET, 然后创建新 ASP.NETWeb 应用程序项目命名 SecureSite。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>将 Label 控件从工具箱拖到 WebForm 1 .aspx Web 窗体, 并将其 ID 属性设置为 greetingLabel 。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>将二 Label 控件拖到 WebForm 1 .aspx, 并将其 ID 属性设置为 certDataLabel 。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>将以下代码添加到 Page _ Load 事件过程：<code>
            <pre class=code>'Visual Basic
            Dim username As String
            userName = User.Identity.Name
            greetingLabel.Text = "Welcome " &amp; userName
            Dim cert As HttpClientCertificate = Request.ClientCertificate
            If cert.IsPresent Then
            'Get the Organization (O) field from the Subject section.
            certDataLabel.Text = cert.Get("Subject O")
            Else
            certDataLabel.Text = "No certificate was found."
            End If
            </pre>
            </code><code>
            <pre class=code>//Visual C#
            string userName;
            userName = User.Identity.Name;
            greetingLabel.Text = "Welcome " + userName;
            HttpClientCertificate cert = Request.ClientCertificate;
            if (cert.IsPresent)
            certDataLabel.Text = cert.Get("SUBJECT O");
            else
            certDataLabel.Text="No certificate was found.";
            </pre>
            </code></td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>生成并保存项目, 并关闭 VisualStudio。</td>
        </tr>
        <tr>
            <td class=number>6.</td>
            <td class=text>启动 InternetExplorer, 然后浏览到以下页面：
            <div class=indent>http://localhost/SecureSite/WebForm1.aspx </div>
            注意， 页显示 " 欢迎 " 并 " 找到无证书 " 消息。 注意， 页不显示用户名称由于有不被验证用户。</td>
        </tr>
        <tr>
            <td class=number>7.</td>
            <td class=text>关闭 InternetExplorer。</td>
        </tr>
    </tbody>
</table>
<br>
<p class=topOfPage><a href="http://support.microsoft.com/kb/315588#top"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></p>
<h3 id=tocHeadRef><span><a id=4></a></span>为基于证书的验证配置 Web 服务器</h3>
<script type=text/javascript>loadTOCNode(2, 'summary');</script>
在本节, 配置 SecureSite 项目以使用证书进行验证。 要使用客户端证书, 必须安装服务器端证书。 可使用现有服务器证书从任何证书机构, 或您可以生成带有 Microsoft 证书服务服务器端证书。 <br><br><br>
<h4 id=tocHeadRef><span><a id=5></a></span>要创建服务器端证书申请</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>On the Start menu, point to Programs, point to Administrative Tools, and then click Internet Services Manager.</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>展开节点用于服务器, 然后单击 默认 Web 站点 。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>在 操作 菜单上, 单击 属性 。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>在 " 目录安全性 选项卡, 单击 服务器证书 。 按照向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>在向导的第一页上单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>服务器证书 上, 创建新证书 , 依次 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>On the Delayed or Immediate Request page, click Prepare the request now, but send it later, and then click Next.</td>
                    </tr>
                    <tr>
                        <td class=number>d. </td>
                        <td class=text>在 名称和安全设置 " 页上, 接受默认设置, 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>e. </td>
                        <td class=text>在 组织信息 页面, 对组织, 键入 MSDN 对于单位, 键入 如何到项目 ， 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>f. </td>
                        <td class=text>您站点的公用名称 上, 键入 localhost , 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>g. </td>
                        <td class=text>On the Geographical Information page, type your country, region, and city details, and then click Next.</td>
                    </tr>
                    <tr>
                        <td class=number>h. </td>
                        <td class=text>证书请求文件名称 上, 接受 （通常 c:\certreq.txt), 默认文件名， 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>i. </td>
                        <td class=text>在 " 请求文件摘要 页上, 确认， 所有详细信息是否正确, 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>j. </td>
                        <td class=text>单击 完成 以关闭向导。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>打开生成, 证书文件， 然后将证书文件的整个内容复制到剪贴板。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=6></a></span>以提交服务器端证书请求</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>启动 InternetExplorer, 然后浏览到以下页面：
            <div class=indent>http://localhost/CertSrv </div>
            <strong>注意 </strong>：： Microsoft 证书服务必须安装。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>按照向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>申请证书 , 依次 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>选择申请类型 上, 高级申请 , 依次 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>在 高级证书申请 页, 单击 提交证书申请使用 base 64 编码 PKCS#10 文件 , 并单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>d. </td>
                        <td class=text>On the Submit a Saved Request page, click in the Base64 Encoded Certificate Request (PKCS #10 or #7) box, and then press the CTRL+V key combination to paste the certificate request that you copied to the clipboard earlier. 单击 提交 。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>关闭 InternetExplorer。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=7></a></span>要颁发服务器端证书</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>在 开始 菜单, 指向 程序 ， 指向 管理工具 , 然后单击 证书机构 。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>展开节点有关证书颁发机构, 然后选择 挂起请求 。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>选择， 您只提交证书申请。 在 操作 菜单, 指向 所有任务 , 然后单击 问题 。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>确认证书颁发证书 " 文件夹, 中显示， 然后双击要查看该证书。</td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>在 详细资料 选项卡, 单击 复制到文件 。 如 Base-64 编码 X 509 证书来 C:\Servercert.cer 保存证书。</td>
        </tr>
        <tr>
            <td class=number>6.</td>
            <td class=text>关闭 属性 对话框为证书。</td>
        </tr>
        <tr>
            <td class=number>7.</td>
            <td class=text>关闭证书机构工具。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=8></a></span>要安装服务器端证书</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>On the Start menu, point to Programs, point to Administrative Tools, and then click Internet Services Manager.</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>展开节点用于服务器, 然后单击 默认 Web 站点 。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>在 操作 菜单上, 单击 属性 。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>在 " 目录安全性 选项卡, 单击 服务器证书 。 按照向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>在向导的第一页上单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>下一步 依次来安装证书, 进程挂起请求 。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>浏览到 C:\Servercert.cer 证书文件， 保存以前。 两次, 单击 下一步 然后单击 完成 。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>单击 确定 以关闭 属性 对话框。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=9></a></span>要为 SSL 和客户端证书配置 SecureSite 站点</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>在 InternetServicesManager, 选择子 SecureSite 站点, 并查看其属性。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>按照向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>在 " 目录安全性 选项卡, 单击 编辑 安全通信 部分中。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>选中 要求安全通道 (SSL) 复选框。 这确保了是加密通信到此站点。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>选中 需要客户证书 复选框。 这可以确保， 站点只能由用户谁有客户端证书安装查看。</td>
                    </tr>
                    <tr>
                        <td class=number>d. </td>
                        <td class=text>选中 " 启用客户证书映射 复选框。 使用将客户证书映射到 Windows 用户帐户。</td>
                    </tr>
                    <tr>
                        <td class=number>e. </td>
                        <td class=text>单击 A0 &gt; " 编辑 " 。 注意， 您可映射到单个 Windows 帐户, 每个证书或可许多证书映射到同一 Windows 帐户。</td>
                    </tr>
                    <tr>
                        <td class=number>f. </td>
                        <td class=text>在 多 1 对 选项卡, 单击 添加 以添加映射规则。 名称 映射规则 , 此规则， 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>g. </td>
                        <td class=text>单击 新建 要创建新规则中 O 子域 （组织） 是证书的主题部分具有 " MSDN " 条件。 单击 确定 以映射应用到其证书包含组织属性对 " MSDN " 用户。 在生产环境, 映射规则严格， 通常验证颁发者是证书以及主题。 请单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>h. </td>
                        <td class=text>在 映射 页, 选择 接受该证书用于身份验证登录 。 单击 浏览 以选择与要映射到 MSDN 用户 Windows 帐户。 对于本例, 使用管理员帐户。 具有受限权限 （实际应用程序, 中您创建专用 Windows 帐户）。 确保您键入正确的密码。</td>
                    </tr>
                    <tr>
                        <td class=number>i. </td>
                        <td class=text>单击 完成 , 并确认密码。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>单击 确定 以关闭 帐户映射 对话框。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>单击 确定 以关闭 " 安全通信 " 对话框中。 If you are prompted to apply the settings to child files and folders, click Select All, and then click OK.</td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>单击 确定 以关闭 SecureSite 属性 对话框。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=10></a></span>要验证基于证书的验证</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>启动 InternetExplorer, 然后浏览到以下页面：
            <div class=indent>https://localhost/SecureSite/WebForm1.aspx </div>
            注意使用安全协议, https。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>确认您收到邮件页需要客户证书。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>关闭 InternetExplorer。</td>
        </tr>
    </tbody>
</table>
<br>
<p class=topOfPage><a href="http://support.microsoft.com/kb/315588#top"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></p>
<h3 id=tocHeadRef><span><a id=11></a></span>安装客户证书</h3>
<script type=text/javascript>loadTOCNode(2, 'summary');</script>
本节, 中您安装客户端证书。 使用来自任何证书机构, 证书也可使用 Microsoft 证书服务以生成自己的证书。 <br><br><br>
<h4 id=tocHeadRef><span><a id=12></a></span>要申请客户端证书</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>启动 InternetExplorer, 然后浏览到以下页面：
            <div class=indent>http://localhost/CertSrv </div>
            </td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>按照向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>申请证书 , 依次 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>在 " 选择申请类型 页, 单击 Web 浏览器证书 , 并单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>键入所需信息。 确保 公司 文本框中， 您键入 MSDN 。</td>
                    </tr>
                    <tr>
                        <td class=number>d. </td>
                        <td class=text>单击以完成请求 提交 。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>关闭 InternetExplorer。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=13></a></span>要颁发客户端证书</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>从 管理工具 程序组启动证书机构工具。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>展开节点有关证书颁发机构, 然后选择 挂起请求 。</td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>选择， 您只提交证书申请。 在 操作 菜单, 指向 所有任务 , 然后单击 问题 。</td>
        </tr>
        <tr>
            <td class=number>4.</td>
            <td class=text>确认证书颁发证书 " 文件夹, 中显示， 然后双击要查看该证书。</td>
        </tr>
        <tr>
            <td class=number>5.</td>
            <td class=text>在 详细资料 选项卡, 单击 复制到文件 。 如 Base-64 编码 X 509 证书来 C:\Clientcert.cer 保存证书。</td>
        </tr>
        <tr>
            <td class=number>6.</td>
            <td class=text>关闭 属性 对话框为证书。</td>
        </tr>
        <tr>
            <td class=number>7.</td>
            <td class=text>关闭证书机构工具。</td>
        </tr>
    </tbody>
</table>
<br>
<h4 id=tocHeadRef><span><a id=14></a></span>要安装客户端证书</h4>
<script type=text/javascript>loadTOCNode(3, 'summary');</script>
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>打开 WindowsExplorer, 并双击要查看证书文件 Clientcert.cer。</td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>按照证书导入向导中这些步骤：
            <table class="list al">
                <tbody>
                    <tr>
                        <td class=number>a. </td>
                        <td class=text>向导, 的第一页上 安装证书 , 依次 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>b. </td>
                        <td class=text>选中复选框, 自动选择证书存储基于证书的类型 ， 然后单击 下一步 。</td>
                    </tr>
                    <tr>
                        <td class=number>c. </td>
                        <td class=text>单击 完成 以完成向导。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td class=number>3.</td>
            <td class=text>取消确认消息框, 然后单击 确定 以关闭证书。</td>
        </tr>
    </tbody>
</table>
<br>
<p class=topOfPage><a href="http://support.microsoft.com/kb/315588#top"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></p>
<h3 id=tocHeadRef><span><a id=15></a></span>验证它工作</h3>
<script type=text/javascript>loadTOCNode(2, 'summary');</script>
本节, 中验证客户应用程序与证书作为公司可指定 " MSDN " 查看 SecureSite 站点。 用户映射到该映射规则中指定帐户。
<table class="list ol">
    <tbody>
        <tr>
            <td class=number>1.</td>
            <td class=text>启动 InternetExplorer, 然后浏览到以下页面：
            <div class=indent>https://localhost/SecureSite/WebForm1.aspx </div>
            </td>
        </tr>
        <tr>
            <td class=number>2.</td>
            <td class=text>确认:
            <table class="list ul">
                <tbody>
                    <tr>
                        <td class=bullet>&#8226;</td>
                        <td class=text>出现 Web 页。</td>
                    </tr>
                    <tr>
                        <td class=bullet>&#8226;</td>
                        <td class=text>是使用适当的 Windows 帐户。</td>
                    </tr>
                    <tr>
                        <td class=bullet>&#8226;</td>
                        <td class=text>从证书读取组织 " MSDN "。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:06 <a href="http://www.cnitblog.com/MartinYao/articles/28360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何使用来自 ASP.NET 的客户端证书调用 Web 服务</title><link>http://www.cnitblog.com/MartinYao/articles/28359.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:03:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28359.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28359.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28359.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28359.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 如何使用来自 ASP.NET 的客户端证书调用 Web 服务发布日期： 10/25/2004 | 更新日期： 10/25/2004Microsoft Corporation目标本模块用于：                         &#8226;                        使用服务组件使 Web 应用程序调用要求客户端使用客...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/28359.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/28359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:03 <a href="http://www.cnitblog.com/MartinYao/articles/28359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Sqlserver配置StateServer</title><link>http://www.cnitblog.com/MartinYao/articles/25980.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 21 Apr 2007 10:36:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/25980.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/25980.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/25980.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/25980.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/25980.html</trackback:ping><description><![CDATA[<p>配置Sqlserver作為SessionState服務器,Open DB:</p>
<p>EXECUTE sp_configure 'show advanced options', 1<br>RECONFIGURE WITH OVERRIDE<br>GO</p>
<p>EXECUTE sp_configure 'Agent XPs', 1<br>RECONFIGURE WITH OVERRIDE<br>GO</p>
<p>EXECUTE sp_configure 'show advanced options', 0<br>RECONFIGURE WITH OVERRIDE<br>GO</p>
<p>C:\Visual Studio 8\SDK\v2.0&gt;aspnet_regsql -S dbserver -U userid&nbsp;-P password<br>sstype c -d database -ssadd</p>
<p>&lt;sessionState <br>&nbsp; mode="SQLServer" <br>&nbsp; allowCustomSqlDatabase="true" <br>&nbsp; sqlConnectionString="data source=dbserver;uid=database ;pwd=userid&nbsp;;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; database=password;pooling=true;"<br>&nbsp; sqlCommandTimeout="30"<br>&nbsp; cookieless="false" <br>&nbsp; timeout="20"<br>/&gt;<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/25980.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-04-21 18:36 <a href="http://www.cnitblog.com/MartinYao/articles/25980.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>asp.net帶權限控制時theme訪問問題 </title><link>http://www.cnitblog.com/MartinYao/articles/25979.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 21 Apr 2007 10:33:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/25979.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/25979.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/25979.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/25979.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/25979.html</trackback:ping><description><![CDATA[<p>&lt;configuration&gt;<br>&nbsp; <br>&nbsp; <br>&nbsp; &lt;system.web&gt;<br>&nbsp;&nbsp;&nbsp; &lt;authentication mode="Forms"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;forms loginUrl="default.aspx" defaultUrl="default.aspx" protection="All"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=".AspxAuth" path="/" timeout="30"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/forms&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/authentication&gt;<br>&nbsp;&nbsp;&nbsp; &lt;authorization&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;deny users="?"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/authorization&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;caching&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;outputCacheSettings&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;outputCacheProfiles&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="MyCache" duration="0" varyByParam="none"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/outputCacheProfiles&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/outputCacheSettings&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/caching&gt;</p>
<p>&nbsp; &lt;/system.web&gt;</p>
<p>&nbsp; &lt;location path="App_Themes/SkinFile"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;system.web&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;authorization&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;allow users="*"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/authorization&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/system.web&gt;<br>&nbsp; &lt;/location&gt;</p>
<p>&nbsp;<br>&lt;/configuration&gt;<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/25979.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-04-21 18:33 <a href="http://www.cnitblog.com/MartinYao/articles/25979.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>An ObjectDataSource/GridView Adapter for Business Entity Collections </title><link>http://www.cnitblog.com/MartinYao/articles/24755.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 27 Mar 2007 05:25:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24755.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24755.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24755.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24755.html</trackback:ping><description><![CDATA[
		<h4 class="BlogPostHeader">An ObjectDataSource/GridView Adapter for Business Entity Collections </h4>
		<div class="BlogPostContent">
				<p>Using ASP.NET 2.0’s new GridView control with the new ObjectDataSource allows you to properly layer your applications.  You can create strongly-typed collections of your business objects, and quickly bind them to the new Grid.  The hype sounds good.  Real good, in fact:</p>
				<p>From <a href="http://msdn.microsoft.com/msdnmag/issues/04/08/GridView/">Move Over DataGrid, There's a New Grid in Town!</a></p>
				<blockquote dir="ltr" style="MARGIN-RIGHT: 0px">
						<p>The GridView control is the successor to the DataGrid and extends it in a number of ways. First, it fully supports data source components and can automatically handle data operations, such as paging, sorting, and editing, provided its bound data source object supports these capabilities.</p>
				</blockquote>
				<p dir="ltr">The key phrase here is “provided its bound data source object <em>supports these capabilities</em>” When you bind your GridView to an ObjectDataSource wired to a DataSet, DataView or DataTable, you do indeed get automatic sorting, and paging, and you can also filter your data through the ObjectDataSource’s FilterExpression property.  All this adds up to a really useful control, one that you can base your entire web applications around.  </p>
				<p dir="ltr">
						<strong>Business Entities and SOA.</strong>
				</p>
				<p dir="ltr">Okay, great. Well, say you’ve been using Business Entities and Strongly-Typed collections <a href="http://www.hanselman.com/blog/ReturningDataSetsFromWebServicesIsTheSpawnOfSatanAndRepresentsAllThatIsTrulyEvilInTheWorld.aspx">in lieu of DataSets</a> to build your application framework and business layers.  If you’ve been developing this way, you’re probably a convert.  Having a nice, clean framework of entities can be a great foundation for application development.  You also may be using a service oriented pattern to return these entity objects, instead of filling your objects with an instance method of the business entity itself.  </p>
				<p dir="ltr">Both of these patterns are good to follow, but neither work out very well using the ObjectDataSource.  If you’ve done this and you try wiring up these collections to an ObjectDataSource and GridView, you’ll find that a Strongly-Typed Collection doesn’t work with the filtering (setting the ObjectDataSource’s FilterExpression property) and automatic sorting capabilities of the GridView.  The GridView’s current implementation requires that you use a DataTable, DataSet or DataView, unless you want to handle these events yourself and do your sorting on your own.  It will automatically do paging, but other than that you’re on your own.</p>
				<p dir="ltr">It also doesn’t like the service oriented/entity pattern much.  It actually works best when you have smart business objects that are DataTables, DataSets or DataViews <strong>and also</strong> implement methods which populate the entities.  In Fact, the ObjectDataSource’s control designer won’t complete until you specify which method on your business object is the “Fill” method. This is too bad, because often what we need is to be able to use our entity objects, our service tier methods, and a nice GridView that does the sorting/paging/filtering stuff easily.  </p>
				<p>Since this doesn’t exist currently, the next best thing is to create a DataTable, fill it with your entity collection’s data, and hand this off to the GridView for presentation.  It’s a little strange having to go back to a DataTable after having built a nice clean framework of entities.  Whenever I find myself having to revert back to using DataSets or DataTables, just for the sake of using one control or another, I feel Michael Corleone in Godfather III -  “Just when I thought that I was out they pull me back in.”  </p>
				<p>This is where I really can jibe with <a href="http://codebetter.com/blogs/jeffrey.palermo/archive/2006/02/20/138778.aspx">Jeffrey’s RAD Kills message</a>.  We have this great RAD grid, but to use it you have to revert to a sometimes poor design choice (DataSets). </p>
				<p dir="ltr">
						<strong>The EntityDataTable – Best of Both Worlds?</strong>
				</p>
				<p dir="ltr">To solve this problem of allowing my business entity collections to be quickly bind-able to a GridView I whipped together a quick abstract adapter class which I can sub-class that is actually a DataTable. This way, when I bind it to a GridView, it will support all this auto-sorting/filtering stuff.   This class has two methods, GetDataTable which uses reflection and returns a DataTable containing a column for each public property in my entity class.  It also contains an abstract method Fill which can be overridden to connect to your service tier, get your collection, and call the GetDataTable method with your collection and your collection’s contained type.</p>
				<div style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 0pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 0pt; FONT-SIZE: 8pt; BACKGROUND: white 0% 50%; PADDING-BOTTOM: 0pt; BORDER-LEFT: windowtext 1pt solid; COLOR: black; PADDING-TOP: 0pt; BORDER-BOTTOM: windowtext 1pt solid; FONT-FAMILY: Courier New; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
						<span style="COLOR: blue">using</span> System; 
<p style="MARGIN: 0px"><span style="COLOR: blue">using</span> System.ComponentModel;</p><p style="MARGIN: 0px"><span style="COLOR: blue">using</span> System.Data;</p><p style="MARGIN: 0px"><span style="COLOR: blue">using</span> System.Reflection;</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px"><span style="COLOR: blue">public</span><span style="COLOR: blue">abstract</span><span style="COLOR: blue">class</span><span style="COLOR: teal">EntityDataTable</span> : <span style="COLOR: teal">DataTable</span></p><p style="MARGIN: 0px">{</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">    [<span style="COLOR: teal">DataObjectMethod</span>(<span style="COLOR: teal">DataObjectMethodType</span>.Select, <span style="COLOR: blue">true</span>)]</p><p style="MARGIN: 0px">    <span style="COLOR: blue">public</span><span style="COLOR: blue">abstract</span><span style="COLOR: teal">DataTable</span> Fill(<span style="COLOR: blue">object</span> parameter);</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">    <span style="COLOR: gray">///</span><span style="COLOR: green"></span><span style="COLOR: gray">&lt;summary&gt;</span></p><p style="MARGIN: 0px">    <span style="COLOR: gray">///</span><span style="COLOR: green"> Gets the data table.</span></p><p style="MARGIN: 0px">    <span style="COLOR: gray">///</span><span style="COLOR: green"></span><span style="COLOR: gray">&lt;/summary&gt;</span></p><p style="MARGIN: 0px">    <span style="COLOR: gray">///</span><span style="COLOR: green"></span><span style="COLOR: gray">&lt;param name="list"&gt;</span><span style="COLOR: green">The list.</span><span style="COLOR: gray">&lt;/param&gt;</span></p><p style="MARGIN: 0px">    <span style="COLOR: gray">///</span><span style="COLOR: green"></span><span style="COLOR: gray">&lt;returns&gt;&lt;/returns&gt;</span></p><p style="MARGIN: 0px">    <span style="COLOR: blue">protected</span><span style="COLOR: teal">DataTable</span> GetDataTable(System.Collections.<span style="COLOR: teal">IList</span> list, <span style="COLOR: teal">Type</span> typ)</p><p style="MARGIN: 0px">    {</p><p style="MARGIN: 0px">        <span style="COLOR: teal">DataTable</span> dt = <span style="COLOR: blue">new</span><span style="COLOR: teal">DataTable</span>();</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">        <span style="COLOR: teal">PropertyInfo</span>[] pi = typ.GetProperties();</p><p style="MARGIN: 0px">        <span style="COLOR: blue">foreach</span> (<span style="COLOR: teal">PropertyInfo</span> p <span style="COLOR: blue">in</span> pi)</p><p style="MARGIN: 0px">        {</p><p style="MARGIN: 0px">            dt.Columns.Add(<span style="COLOR: blue">new</span><span style="COLOR: teal">DataColumn</span>(p.Name, p.PropertyType));</p><p style="MARGIN: 0px">        }</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">        <span style="COLOR: blue">foreach</span> (<span style="COLOR: blue">object</span> obj <span style="COLOR: blue">in</span> list)</p><p style="MARGIN: 0px">        {</p><p style="MARGIN: 0px">            <span style="COLOR: blue">object</span>[] row = <span style="COLOR: blue">new</span><span style="COLOR: blue">object</span>[pi.Length];</p><p style="MARGIN: 0px">            <span style="COLOR: blue">int</span> i = 0;</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">            <span style="COLOR: blue">foreach</span> (<span style="COLOR: teal">PropertyInfo</span> p <span style="COLOR: blue">in</span> pi)</p><p style="MARGIN: 0px">            {</p><p style="MARGIN: 0px">                row[i++] = p.GetValue(obj, <span style="COLOR: blue">null</span>);</p><p style="MARGIN: 0px">            }</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">            dt.Rows.Add(row);</p><p style="MARGIN: 0px">        }</p><p style="MARGIN: 0px"> </p><p style="MARGIN: 0px">        <span style="COLOR: blue">return</span> dt;</p><p style="MARGIN: 0px">    }</p><p style="MARGIN: 0px">}</p></div>
				<p>
						<strong>EntityDataTable in Action</strong>
				</p>
				<p>Here’s a quick example of a derived class, that adpts my SomethingCollection to easily work with the GridView and ObjectDataSource.</p>
				<div style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 0pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 0pt; FONT-SIZE: 8pt; BACKGROUND: white 0% 50%; PADDING-BOTTOM: 0pt; BORDER-LEFT: windowtext 1pt solid; COLOR: black; PADDING-TOP: 0pt; BORDER-BOTTOM: windowtext 1pt solid; FONT-FAMILY: Courier New; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
						<p style="MARGIN: 0px">    <span style="COLOR: blue">public</span><span style="COLOR: blue">class</span><span style="COLOR: teal">BindableSomethingCollection</span> : <span style="COLOR: teal">EntityDataTable</span></p>
						<p style="MARGIN: 0px">    {</p>
						<p style="MARGIN: 0px">        <span style="COLOR: blue">public</span><span style="COLOR: blue">override</span><span style="COLOR: teal">DataTable</span> Fill(<span style="COLOR: blue">object</span> parameter)</p>
						<p style="MARGIN: 0px">        {</p>
						<p style="MARGIN: 0px">            <span style="COLOR: teal">SomeService</span> somesrv = <span style="COLOR: blue">new</span><span style="COLOR: teal">SomeService</span>();</p>
						<p style="MARGIN: 0px">            <span style="COLOR: blue">return</span> GetDataTable(somesrv.GetSomeCollection(parameter as <font color="#008080">SomeType</font>), <span style="COLOR: blue">typeof</span>(<font color="#008080">SomeContainedType</font>));</p>
						<p style="MARGIN: 0px">        }        </p>
						<p style="MARGIN: 0px">    }</p>
				</div>
				<p>And here’s a quick ASCX control snippet that shows it all tied together:</p>
				<font color="#008080">
						<font color="#0000ff" size="2">
						</font>
						<p>
								<font color="#0000ff" size="2">&lt;</font>
								<font color="#800000" size="2">asp</font>
								<font color="#0000ff" size="2">:</font>
								<font color="#800000" size="2">GridView</font>
								<font size="2">
										<font color="#000000">
										</font>
								</font>
								<font color="#ff0000" size="2">ID</font>
								<font size="2">
								</font>
								<font color="#0000ff" size="2">="GridView1"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">runat</font>
								<font color="#0000ff" size="2">="server"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">AutoGenerateColumns</font>
								<font color="#0000ff" size="2">="True"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">EmptyDataText</font>
								<font color="#0000ff" size="2">="No Records" </font>
								<font color="#ff0000" size="2">ShowFooter</font>
								<font size="2">
								</font>
								<font color="#0000ff" size="2">="True" </font>
								<font color="#ff0000" size="2">AllowPaging</font>
								<font size="2">
								</font>
								<font color="#0000ff" size="2">="True"</font>
								<font size="2">  </font>
								<font color="#ff0000" size="2">AllowSorting</font>
								<font color="#0000ff" size="2">="True"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">DataSourceID</font>
								<font color="#0000ff" size="2">="ObjectDataSource1"</font>
								<font size="2"> </font>
								<font color="#0000ff" size="2">&gt;</font>
								<font color="#0000ff" size="2">&lt;/</font>
								<font color="#800000" size="2">asp</font>
								<font color="#0000ff" size="2">:</font>
								<font color="#800000" size="2">GridView</font>
								<font color="#0000ff" size="2">&gt;</font>
						</p>
						<font color="#0000ff" size="2">
						</font>
						<font color="#0000ff" size="2">
						</font>
						<p>
								<font color="#0000ff" size="2">&lt;</font>
								<font color="#800000" size="2">asp</font>
								<font color="#0000ff" size="2">:</font>
								<font color="#800000" size="2">ObjectDataSource</font>
								<font color="#000000" size="2">
								</font>
								<font color="#ff0000" size="2">ID</font>
								<font color="#0000ff" size="2">="ObjectDataSource1"</font>
								<font color="#000000" size="2">
								</font>
								<font color="#ff0000" size="2">runat</font>
								<font color="#0000ff" size="2">="server"</font>
								<font color="#000000" size="2">
								</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">SelectMethod</font>
								<font color="#0000ff" size="2">="Fill"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">TypeName</font>
								<font color="#0000ff" size="2">="BindableSomethingCollection"</font>
								<font color="#0000ff" size="2">&gt;<br /></font>
								<font color="#0000ff" size="2">  &lt;</font>
								<font color="#800000" size="2">SelectParameters</font>
								<font color="#0000ff" size="2">&gt;<br /></font>
								<font color="#0000ff" size="2">    &lt;</font>
								<font color="#800000" size="2">asp</font>
								<font color="#0000ff" size="2">:</font>
								<font color="#800000" size="2">SessionParameter</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">DefaultValue</font>
								<font color="#0000ff" size="2">="null"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">Name</font>
								<font color="#0000ff" size="2">="parameter"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">SessionField</font>
								<font color="#0000ff" size="2">="PARAMETER"</font>
								<font size="2">
								</font>
								<font color="#ff0000" size="2">Type</font>
								<font color="#0000ff" size="2">="Object"</font>
								<font size="2">
								</font>
								<font color="#0000ff" size="2">/&gt;<br /></font>
								<font color="#0000ff" size="2">  &lt;/</font>
								<font color="#800000" size="2">SelectParameters</font>
								<font color="#0000ff" size="2">&gt;<br />&lt;/</font>
								<font color="#800000" size="2">asp</font>
								<font color="#0000ff" size="2">:</font>
								<font color="#800000" size="2">ObjectDataSource</font>
								<font color="#0000ff" size="2">&gt;</font>
						</p>
				</font>
				<p>All that's left for me to do is pop my parameter in the session, and call GridView1.DataBind().. Voila!  My grid is bound to my entity collection, sorting and paging work like a charm, and I can filter my data buy calling by setting the Filter parameter of the ObjectDataSource.</p>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-27 13:25 <a href="http://www.cnitblog.com/MartinYao/articles/24755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在client端通过java script调用Web Service </title><link>http://www.cnitblog.com/MartinYao/articles/24718.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 27 Mar 2007 00:59:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24718.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24718.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24718.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24718.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 以下代码实现了在客户端用java script调用Web Service，通过对Web Service：TimeService中GetTime()方法的调用，在客户端显示服务器当前时间，并且以1秒为间隔自动刷新。		TimeService: GetTime()																		//						Return time on server							...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/24718.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/24718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-27 08:59 <a href="http://www.cnitblog.com/MartinYao/articles/24718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax C#.NET simple chat:</title><link>http://www.cnitblog.com/MartinYao/articles/24710.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 26 Mar 2007 14:53:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24710.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24710.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24710.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24710.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/useritems/Ajax_Chat/AjaxChat.zip">
								<font color="#002c99">Download source files - 67 Kb</font>
						</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/useritems/Ajax_Chat/AjaxChat.zip">
								<font color="#002c99">Download demo project - 67 Kb</font>
						</a>
				</li>
		</ul>
		<p>
				<img height="461" alt="Sample Image - SimpleAjaxChat.jpg" src="http://www.codeproject.com/useritems/Ajax_Chat/SimpleAjaxChat.jpg" width="740" />
		</p>
		<h2>Introduction</h2>This is a simple Ajax based chat done in c#. <br /><h3>Instructions</h3>Important Note: This is a chat made for ASP.NET 2.0 (VS2005 or later). 
<ol><li nd="2">Add a new <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/useritems/Ajax_Chat.asp#" target="_blank" itxtdid="3266088">web</a> form to your solution, name it ChatPage.aspx, choose C# language. 
</li><li nd="3">Copy and paste the "Page html" to the source view of you web form. 
</li><li nd="4">Copy and paste the "Page code behind" code to the code behind file of your web form named ChatPage.aspx.cs. 
</li><li nd="5">Copy and paste the "Business logic" to a new class in your App_Code folder named SPilafisChatLogic.cs. 
</li><li nd="6">Add <a href="http://ajax.schwarz-interactive.de/csharpsample/" target="_blank"><font color="#002c99">Ajax</font></a> for ASP.NET 2.0 (VS2005) to your solution. (With VS2005 you just have to drag and drop the dll in your bin folder and add a couple of lines to your web.config) 
</li><li nd="7">Get some french fries and enjoy! </li></ol><h3>Page html</h3><div class="precollapse" id="premain0" style="WIDTH: 100%"><img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" /><span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="8" preid="0"> Collapse</span></div><pre id="pre0" style="MARGIN-TOP: 0px" nd="9">&lt;%@ Page Language=<span class="cpp-string" nd="10">"C#"</span> AutoEventWireup=<span class="cpp-string" nd="11">"true"</span> CodeFile=<span class="cpp-string" nd="12">"ChatPage.aspx.cs"</span> Inherits=<span class="cpp-string" nd="13">"ChatPage"</span> %&gt;

&lt;!DOCTYPE html PUBLIC <span class="cpp-string" nd="14">"-//W3C//DTD XHTML 1.0 Transitional//EN"</span><span class="cpp-string" nd="15">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span>&gt;

&lt;html xmlns=<span class="cpp-string" nd="16">"http://www.w3.org/1999/xhtml"</span> xml:lang=<span class="cpp-string" nd="17">"en"</span> &gt;
&lt;head id=<span class="cpp-string" nd="18">"Head1"</span> runat=<span class="cpp-string" nd="19">"<a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/useritems/Ajax_Chat.asp#" target="_blank" itxtdid="3563345">server</a>"</span>&gt;
    &lt;title&gt;Chat Ajax .NET&lt;/title&gt;
    &lt;style type=<span class="cpp-string" nd="20">"text/css"</span>&gt;
    td {
        font-family: Arial;
        font-size: 8pt;
       }
    &lt;/style&gt;
    &lt;!--
    License and Disclaimer: All the intructions, code, html and everything presented with this solution is provided 'as is' with no warranties what so ever.
	The only condition for you to use this software is that you keep the link to http:<span class="cpp-comment">//www.spilafis.com.ar in the chat page as provided. Please support freeware keeping the link and clicking on my sponsors.</span>
	Thank you!
    !--&gt;
&lt;/head&gt;
&lt;body&gt;

	&lt;form id=<span class="cpp-string" nd="21">"form1"</span> runat=<span class="cpp-string" nd="22">"server"</span> onsubmit=<span class="cpp-string" nd="23">"return false;"</span>&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    &lt;table align=<span class="cpp-string" nd="24">"center"</span> width=<span class="cpp-string" nd="25">"732"</span> border=<span class="cpp-string" nd="26">"0"</span> cellpadding=<span class="cpp-string" nd="27">"0"</span> cellspacing=<span class="cpp-string" nd="28">"0"</span>&gt;
    &lt;tr&gt;
        &lt;td&gt;
        &lt;textarea id=<span class="cpp-string" nd="29">"txtChatFrame"</span> runat=<span class="cpp-string" nd="30">"server"</span> rows=<span class="cpp-string" nd="31">"15"</span> style=<span class="cpp-string" nd="32">"width:527px; border:1px solid #aaaaaa; padding:4px;"</span> readonly=<span class="cpp-string" nd="33">"readonly"</span>&gt;&lt;/textarea&gt;&lt;br/&gt;
        &lt;/td&gt;
        &lt;td valign=<span class="cpp-string" nd="34">"top"</span>&gt;
            &lt;div style=<span class="cpp-string" nd="35">"overflow: auto; height: 226px; border-top: solid 2px gray; border-bottom: solid 2px gray; border-right: solid 1px gray; border-left: solid 1px gray; "</span>&gt;
                &lt;asp:GridView ID=<span class="cpp-string" nd="36">"grvUsers"</span> runat=<span class="cpp-string" nd="37">"server"</span> AutoGenerateColumns=<span class="cpp-string" nd="38">"False"</span> BackColor=<span class="cpp-string" nd="39">"White"</span>
                    BorderColor=<span class="cpp-string" nd="40">"#DEDFDE"</span> BorderStyle=<span class="cpp-string" nd="41">"None"</span> BorderWidth=<span class="cpp-string" nd="42">"1px"</span> CellPadding=<span class="cpp-string" nd="43">"1"</span> ForeColor=<span class="cpp-string" nd="44">"Black"</span>
                    GridLines=<span class="cpp-string" nd="45">"Vertical"</span> Height=<span class="cpp-string" nd="46">"30px"</span>&gt;
                    &lt;FooterStyle BackColor=<span class="cpp-string" nd="47">"#CCCC99"</span> /&gt;
                    &lt;Columns&gt;
                        &lt;asp:BoundField HeaderText=<span class="cpp-string" nd="48">"User"</span> ReadOnly=<span class="cpp-string" nd="49">"True"</span> DataField=<span class="cpp-string" nd="50">"ChatUsers"</span>&gt;
                            &lt;ItemStyle Width=<span class="cpp-string" nd="51">"110px"</span> /&gt;
                            &lt;HeaderStyle Height=<span class="cpp-string" nd="52">"10px"</span> /&gt;
                        &lt;/asp:BoundField&gt;
                        &lt;asp:BoundField HeaderText=<span class="cpp-string" nd="53">"Last Activity"</span> DataField=<span class="cpp-string" nd="54">"ChatLastActivity"</span>&gt;
                            &lt;ItemStyle Width=<span class="cpp-string" nd="55">"190px"</span> /&gt;
                            &lt;HeaderStyle Height=<span class="cpp-string" nd="56">"10px"</span> /&gt;
                        &lt;/asp:BoundField&gt;
                    &lt;/Columns&gt;
                    &lt;RowStyle BackColor=<span class="cpp-string" nd="57">"#F7F7DE"</span> /&gt;
                    &lt;SelectedRowStyle BackColor=<span class="cpp-string" nd="58">"#CE5D5A"</span> Font-Bold=<span class="cpp-string" nd="59">"True"</span> ForeColor=<span class="cpp-string" nd="60">"White"</span> /&gt;
                    &lt;PagerStyle BackColor=<span class="cpp-string" nd="61">"#F7F7DE"</span> ForeColor=<span class="cpp-string" nd="62">"Black"</span> HorizontalAlign=<span class="cpp-string" nd="63">"Right"</span> /&gt;
                    &lt;HeaderStyle BackColor=<span class="cpp-string" nd="64">"#6B696B"</span> Font-Bold=<span class="cpp-string" nd="65">"True"</span> ForeColor=<span class="cpp-string" nd="66">"White"</span> /&gt;
                    &lt;AlternatingRowStyle BackColor=<span class="cpp-string" nd="67">"White"</span> /&gt;
                &lt;/asp:GridView&gt;
            &lt;/div&gt;
            &lt;input id=<span class="cpp-string" nd="68">"btnRefresh"</span> runat=<span class="cpp-string" nd="69">"server"</span> type=<span class="cpp-string" nd="70">"submit"</span> onclick=<span class="cpp-string" nd="71">"document.forms['form1'].submit();"</span> value=<span class="cpp-string" nd="72">"Refresh"</span> style=<span class="cpp-string" nd="73">"cursor:pointer;border:1px solid gray; height: 15px; width: 100%; font-size: 7pt;"</span> size=<span class="cpp-string" nd="74">"20"</span>/&gt;
            &lt;/td&gt;            
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;
        
        &lt;table border=<span class="cpp-string" nd="75">"0"</span> cellpadding=<span class="cpp-string" nd="76">"0"</span> cellspacing=<span class="cpp-string" nd="77">"5"</span> style=<span class="cpp-string" nd="78">"width:527px;"</span>&gt;
        &lt;tr&gt;
            &lt;td align=<span class="cpp-string" nd="79">"right"</span> valign=<span class="cpp-string" nd="80">"middle"</span>&gt;Message: &lt;/td&gt;
            &lt;td align=<span class="cpp-string" nd="81">"left"</span> style=<span class="cpp-string" nd="82">"height: 45px"</span> valign=<span class="cpp-string" nd="83">"top"</span>&gt;
                &lt;table border=<span class="cpp-string" nd="84">"0"</span> cellpadding=<span class="cpp-string" nd="85">"0"</span> cellspacing=<span class="cpp-string" nd="86">"2"</span>&gt;
                &lt;tr&gt;
                    &lt;td&gt;&lt;textarea cols=<span class="cpp-string" nd="87">"70"</span> rows=<span class="cpp-string" nd="88">"2"</span> id=<span class="cpp-string" nd="89">"txtMssg"</span> style=<span class="cpp-string" nd="90">"border:1px solid gray; width: 370px; height: 37px;"</span> onkeyup=<span class="cpp-string" nd="91">"SendByKey(event.keyCode);"</span>&gt;&lt;/textarea&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;input id=<span class="cpp-string" nd="92">"btnSend"</span> type=<span class="cpp-string" nd="93">"button"</span> value=<span class="cpp-string" nd="94">"Send"</span> onclick=<span class="cpp-string" nd="95">"Send()"</span> style=<span class="cpp-string" nd="96">"cursor:pointer;border:1px solid gray; height: 42px;"</span>/&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;/table&gt;&lt;/td&gt;  
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=<span class="cpp-string" nd="97">"right"</span> valign=<span class="cpp-string" nd="98">"middle"</span> style=<span class="cpp-string" nd="99">"width:70px"</span>&gt;User Name: &lt;/td&gt;
            &lt;td align=<span class="cpp-string" nd="100">"left"</span>&gt;
                &lt;table border=<span class="cpp-string" nd="101">"0"</span> cellpadding=<span class="cpp-string" nd="102">"0"</span> cellspacing=<span class="cpp-string" nd="103">"2"</span>&gt;
                &lt;tr&gt;
                    &lt;td&gt;&lt;input id=<span class="cpp-string" nd="104">"txtName"</span> type=<span class="cpp-string" nd="105">"text"</span> maxlength=<span class="cpp-string" nd="106">"10"</span> style=<span class="cpp-string" nd="107">"border:1px solid #aaaaaa; width: 145px;"</span> onblur=<span class="cpp-string" nd="108">"ChangeUserName(this);"</span>/ value=<span class="cpp-string" nd="109">"&lt;%=SpilafisChatLogic.Chat.GetUserName()%&gt;"</span> /&gt;
                    &lt;/td&gt;
                    &lt;td&gt;
                        &amp;nbsp;&amp;nbsp; Get source code!: &lt;a href=<span class="cpp-string" nd="110">"http://www.spilafis.com.ar"</span>&gt;http:<span class="cpp-comment">//www.spilafis.com.ar&lt;/a&gt;</span>
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;/table&gt;
            &lt;/td&gt;  
        &lt;/tr&gt;
        &lt;/table&gt;

        &lt;/td&gt;
        &lt;td valign=<span class="cpp-string" nd="111">"middle"</span>&gt;
            &lt;br /&gt;
            &lt;table border=<span class="cpp-string" nd="112">"0"</span> cellpadding=<span class="cpp-string" nd="113">"0"</span> cellspacing=<span class="cpp-string" nd="114">"0"</span>&gt;
                &lt;tr&gt;
                    &lt;td align=<span class="cpp-string" nd="115">"center"</span>&gt;Users Writing: &amp;nbsp;&amp;nbsp; &lt;br /&gt;(max. &lt;%=SpilafisChatLogic.Chat.MaxLoggedInUsers.ToString()%&gt;) &lt;/td&gt;
                    &lt;td align=<span class="cpp-string" nd="116">"right"</span>&gt;&lt;span id=<span class="cpp-string" nd="117">"spnW"</span>&gt;0&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td colspan=<span class="cpp-string" nd="118">"2"</span>&gt;&amp;nbsp;&lt;br /&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td align=<span class="cpp-string" nd="119">"left"</span>&gt;Users Reading: &amp;nbsp;&amp;nbsp; &lt;/td&gt;
                    &lt;td align=<span class="cpp-string" nd="120">"right"</span>&gt;&lt;span id=<span class="cpp-string" nd="121">"spnR"</span>&gt;0&lt;/span&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;/table&gt;

    &lt;br /&gt;
    
    &lt;br /&gt;


&lt;script type=<span class="cpp-string" nd="122">"text/javascript"</span> language=<span class="cpp-string" nd="123">"javascript"</span>&gt;

function ChangeUserName(otxtName)
{
    var tentative_name = otxtName.value;
    if(tentative_name==<span class="cpp-string" nd="124">""</span>)
        alert(<span class="cpp-string" nd="125">"You must enter a user name to be able to chat."</span>);
    else
    {
        if(tentative_name!=last_user_name)
        {
            var otxtMssg = document.getElementById(<span class="cpp-string" nd="126">"txtMssg"</span>);
            var obtnSend = document.getElementById(<span class="cpp-string" nd="127">"btnSend"</span>);

            var ret = Chat.ChangeUserName(tentative_name);
            if(ret!=null &amp;&amp; ret.error!=null &amp;&amp; ret.error!=<span class="cpp-string" nd="128">""</span>)
            {
                otxtName.value = last_user_name;
                alert(new String(ret.error).replace(<span class="cpp-string" nd="129">"System.Exception "</span>,<span class="cpp-string" nd="130">""</span>));
            }
            else
            {
                last_user_name = otxtName.value;
                obtnSend.disabled = false;
                otxtMssg.disabled = false;
                otxtMssg.focus();
            }
        }
    }
}

function Send()
{
    var otxtName = document.getElementById(<span class="cpp-string" nd="131">"txtName"</span>);
    if(otxtName.value!=<span class="cpp-string">""</span>)
    {
        var otxtMssg = document.getElementById(<span class="cpp-string">"txtMssg"</span>);
        if(!otxtMssg.disabled)
        {
            var ret = Chat.Post(otxtMssg.value);
            if(ret!=null &amp;&amp; ret.error!=null)
            {
                alert(new String(ret.error).replace(<span class="cpp-string">"System.Exception "</span>,<span class="cpp-string">""</span>));
            }
            otxtMssg.value = <span class="cpp-string">""</span>;
            otxtMssg.focus();
        }
    }
    else
    {
        if(!otxtName.disabled)
        {
            alert(<span class="cpp-string">"Enter a user name"</span>);
            otxtName.focus();
        }
    }
}

function SendByKey(keycode)
{
    if (keycode == 13)
        Send();
    event.returnValue = false;
}

var cycles = 20;
var add_wait_cycles = 20;
var reload_wait = 700;
var last_user_name = <span class="cpp-string">""</span>;
var max_chat_lines = &lt;%=SpilafisChatLogic.Chat.MaxChatLines%&gt;;
function Reload()
{
    var ret;
    cycles++;;
    if(cycles&gt;=add_wait_cycles)
    {
        cycles = 0;

        <span class="cpp-comment">// Get users writing</span>
        ret = Chat.GetUsersWriting();
        if(ret!=null &amp;&amp; (ret.error==null || ret.error==<span class="cpp-string">""</span>) &amp;&amp; ret.value!=null &amp;&amp; ret.value!=<span class="cpp-string">""</span>)
            document.getElementById(<span class="cpp-string">"spnW"</span>).innerHTML = ret.value;

        <span class="cpp-comment">// Get users reading</span>
        ret = Chat.GetUsersReading();
        if(ret!=null &amp;&amp; (ret.error==null || ret.error==<span class="cpp-string">""</span>) &amp;&amp; ret.value!=null &amp;&amp; ret.value!=<span class="cpp-string">""</span>)
            document.getElementById(<span class="cpp-string">"spnR"</span>).innerHTML = ret.value;
    }

    <span class="cpp-comment">// If not logged in to write then try to logg in the user</span>
    var otxtMssg = document.getElementById(<span class="cpp-string">"txtMssg"</span>);
    if(otxtMssg.disabled)
    {
        var otxtName = document.getElementById(<span class="cpp-string">"txtName"</span>);
        if(otxtName.value==<span class="cpp-string">""</span>)
        {
            ret = Chat.GetNextUserName();
            if(ret!=null &amp;&amp; ret.error==null &amp;&amp; ret.value!=null &amp;&amp; ret.value!=<span class="cpp-string">""</span>)
            {
                    otxtName.value = ret.value;
                    LoggedIn();
                    alert(<span class="cpp-string">"You have been logged in"</span>);
            }
        }
        else
        {
            last_user_name = otxtName.value;
            otxtMssg.disabled = false;
            var obtnSend = document.getElementById(<span class="cpp-string">"obtnSend"</span>);
            obtnSend.disabled = false;
            otxtMssg.focus();
        }
    }

    <span class="cpp-comment">// Read Chat</span>
    var ret = Chat.Read();
    if(ret!=null &amp;&amp; ret.error!=null)
    {
        alert(<span class="cpp-string">"A critical error ocurred: "</span> + new String(ret.error).replace(<span class="cpp-string">"System.Exception "</span>,<span class="cpp-string">""</span>) + <span class="cpp-string">"\nPlease reload page to try again."</span>);
        return;
    }
    var value = ret.value;
    if(value!=null &amp;&amp; value!=<span class="cpp-string">""</span>)
    {
        var otxtChatFrame = document.getElementById(<span class="cpp-string">"&lt;%=txtChatFrame.ClientID%&gt;"</span>);
        var already_read = otxtChatFrame.value;
        <span class="cpp-comment">//var all = value + already_read;</span>
        var all = already_read + value;
        if(all!=<span class="cpp-string">""</span>)
        {
            var arr = all.split(<span class="cpp-string">"\n"</span>);
            if(arr.length&gt;max_chat_lines)
            {
                all = <span class="cpp-string">""</span>;
                <span class="cpp-comment">//for(var i=0; i&lt;max_chat_lines; i++)</span>
                for(var i=(arr.length-max_chat_lines); i&lt;arr.length; i++)
                    all += arr[i] + <span class="cpp-string">"\n"</span>;
            }
        }    
        otxtChatFrame.value = all;
        <span class="cpp-comment">// Scroll down to the last read line</span>
        otxtChatFrame.scrollTop=otxtChatFrame.scrollHeight;
    }

    window.setTimeout(<span class="cpp-string">"Reload();"</span>, reload_wait);
}

function Start()
{
    Reload();

    <span class="cpp-comment">// Initial scroll down to the last read line (in case Refresh is hit)</span>
    var otxtChatFrame = document.getElementById(<span class="cpp-string">"&lt;%=txtChatFrame.ClientID%&gt;"</span>);
    otxtChatFrame.scrollTop=otxtChatFrame.scrollHeight;
}

function LoggedIn()
{
    var otxtName = document.getElementById(<span class="cpp-string">"txtName"</span>);
    var obtnSend = document.getElementById(<span class="cpp-string">"btnSend"</span>);
    var otxtMssg = document.getElementById(<span class="cpp-string">"txtMssg"</span>);
    last_user_name = otxtName.value;
    otxtName.disabled = false;
    obtnSend.disabled = false;
    otxtMssg.disabled = false;
    otxtMssg.focus();
}

<span class="cpp-comment">// Call to start!</span>
Start();
&lt;/script&gt;


	
	&lt;/form&gt;
	
&lt;/body&gt;
&lt;/html&gt;


</pre><h3>Page code behind</h3><br /><div class="precollapse" id="premain1" style="WIDTH: 100%"><img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" /><span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="1"> Collapse</span></div><pre id="pre1" style="MARGIN-TOP: 0px">using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;
using System.Globalization;

<span class="cpp-comment">/// &lt;summary&gt;</span><span class="cpp-comment">/// License and Disclaimer: All the intructions, code, html and everything presented with this solution is provided 'as is' with no warranties what so ever.</span><span class="cpp-comment">/// The only condition for you to use this software is that you keep the link to http://www.spilafis.com.ar in the chat page as provided. Please support freeware keeping the link and clicking on my sponsors.</span><span class="cpp-comment">/// Thank you!</span><span class="cpp-comment">/// &lt;/summary&gt;</span>
public partial class ChatPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        <span class="cpp-comment">// Register AJAX</span>
        Ajax.Utility.RegisterTypeForAjax(typeof(SpilafisChatLogic.Chat));
        
        UpdateUsersGridView();
    }

    public void UpdateUsersGridView()
    {
        <span class="cpp-comment">// Update grid</span>
        grvUsers.DataSource = SpilafisChatLogic.Chat.GetUsersDataSource();
        grvUsers.DataBind();
    }
    protected void btnRefresh_ServerClick(object sender, EventArgs e)
    {
        UpdateUsersGridView();
    }
}


</pre><h3>Business logic</h3><p></p><div class="precollapse" id="premain2" style="WIDTH: 100%"><img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" /><span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="2"> Collapse</span></div><pre id="pre2" style="MARGIN-TOP: 0px">using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;
using System.Globalization;

namespace SpilafisChatLogic
{
    <span class="cpp-comment">/// &lt;summary&gt;</span><span class="cpp-comment">/// License and Disclaimer: All the intructions, code, html and everything presented with this solution is provided 'as is' with no warranties what so ever.</span><span class="cpp-comment">/// The only condition for you to use this software is that you keep the link to http://www.spilafis.com.ar in the chat page as provided. Please support freeware keeping the link and clicking on my sponsors.</span><span class="cpp-comment">/// Thank you!</span><span class="cpp-comment">/// &lt;/summary&gt;</span>
    public class Business
    {
        public Business()
        {
        }

        public static User CurrentUser
        {
            get
            {
                if (HttpContext.Current.Session[<span class="cpp-string">"ChatCurrentUser"</span>] == null)
                    HttpContext.Current.Session[<span class="cpp-string">"ChatCurrentUser"</span>] = new User();

                return (User)HttpContext.Current.Session[<span class="cpp-string">"ChatCurrentUser"</span>];
            }
            set
            {
                HttpContext.Current.Session[<span class="cpp-string">"ChatCurrentUser"</span>] = value;
            }
        }

        public static App CurrentApp
        {
            get
            {
                if (HttpContext.Current.Application[<span class="cpp-string">"ChatCurrentApp"</span>] == null)
                    HttpContext.Current.Application[<span class="cpp-string">"ChatCurrentApp"</span>] = new App();

                return (App)HttpContext.Current.Application[<span class="cpp-string">"ChatCurrentApp"</span>];
            }
            set
            {
                HttpContext.Current.Application[<span class="cpp-string">"ChatCurrentApp"</span>] = value;
            }
        }
    }

    public class User
    {
<span class="cpp-preprocessor">        #region Private Members</span>

        private bool m_ajax_chat_counted;
        private string m_ajax_chat_user_name;
        private int m_ajax_chat_last_read_id;

        private bool m_code_access_enabled;

<span class="cpp-preprocessor">        #endregion</span>

        public User()
        {
            m_ajax_chat_counted = false;
            m_ajax_chat_user_name = <span class="cpp-string">""</span>;
            m_ajax_chat_last_read_id = -1;

            m_code_access_enabled = false;
        }

<span class="cpp-preprocessor">        #region Properties</span>

        public bool AjaxChatCounted
        {
            get { return m_ajax_chat_counted; }
            set { m_ajax_chat_counted = value; }
        }

        public string AjaxChatUserName
        {
            get { return m_ajax_chat_user_name; }
            set { m_ajax_chat_user_name = value; }
        }

        public int AjaxChatLastReadId
        {
            get { return m_ajax_chat_last_read_id; }
            set { m_ajax_chat_last_read_id = value; }
        }

<span class="cpp-preprocessor">        #endregion</span>

    }


    public class App
    {
<span class="cpp-preprocessor">        #region Private Members</span>

        private int m_ajax_chat_users_r;
        private Hashtable m_ajax_chat_users_w;
        private Hashtable m_ajax_chat_users_w_last_activity;
        private ArrayList m_ajax_chat;

<span class="cpp-preprocessor">        #endregion</span>


        public App()
        {
            m_ajax_chat_users_w = new Hashtable();
            m_ajax_chat_users_w_last_activity = new Hashtable();
            m_ajax_chat = new ArrayList();
        }


<span class="cpp-preprocessor">        #region Properties</span>

        public int AjaxChatUsersR
        {
            get { return m_ajax_chat_users_r; }
            set { m_ajax_chat_users_r = value; }
        }

        public Hashtable AjaxChatUsersW
        {
            get { return m_ajax_chat_users_w; }
            set { m_ajax_chat_users_w = value; }
        }

        public Hashtable AjaxChatUsersWLastActivity
        {
            get { return m_ajax_chat_users_w_last_activity; }
            set { m_ajax_chat_users_w_last_activity = value; }
        }

        public ArrayList AjaxChat
        {
            get { return m_ajax_chat; }
            set { m_ajax_chat = value; }
        }

<span class="cpp-preprocessor">        #endregion</span>
    }


    public class Chat
    {
<span class="cpp-preprocessor">        #region Constants</span>

        public static int MaxMessageLength = 100;
        public static int MaxChatLines = 50;
        public static int MaxLoggedInUsers = 5;
        public static string[] BadWords = { <span class="cpp-string">"a**hole"</span>, <span class="cpp-string">"boludo"</span>}; <span class="cpp-comment">// Add your bad word dictionary here</span><span class="cpp-preprocessor">        #endregion</span>

        public Chat()
        {
        }

<span class="cpp-preprocessor">        #region Methods</span>

        private static void IncrementUsersReading()
        {
            Business.CurrentApp.AjaxChatUsersR = GetIntUsersReading() + 1;
        }

        private static int GetIntUsersReading()
        {
            return Business.CurrentApp.AjaxChatUsersR;
        }

        private static int GetId(string message)
        {
            return Convert.ToInt32(message.Split('-')[0]);
        }

        private static string GetMessage(string message)
        {
            return message.Split('-')[1];
        }

        private static string GetNextId(ArrayList ajax_chat_list)
        {
            int id = GetMaxId(ajax_chat_list);
            if (id &lt; Int32.MaxValue)
                id++;
            else
                id = 0;
            return id.ToString() + <span class="cpp-string">"-"</span>;
        }

        private static int GetMaxId(ArrayList ajax_chat_list)
        {
            if (ajax_chat_list.Count &gt; 0)
                return GetId(ajax_chat_list[0].ToString());
            else
                return 0;
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string GetUserName()
        {
            if (Business.CurrentUser.AjaxChatUserName == <span class="cpp-string">""</span>)
                Chat.GetNextUserName();
            return Business.CurrentUser.AjaxChatUserName;
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string GetNextUserName()
        {
            string user_name = <span class="cpp-string">""</span>;
            <span class="cpp-comment">// Count user reading if not counted yet</span>
            if (!Business.CurrentUser.AjaxChatCounted)
            {
                Business.CurrentUser.AjaxChatCounted = true;
                IncrementUsersReading();  <span class="cpp-comment">// Is decremented in session end</span>
            }
            else
            {
                if (Business.CurrentUser.AjaxChatUserName != null &amp;&amp; Business.CurrentUser.AjaxChatUserName != <span class="cpp-string">""</span>)
                    return Business.CurrentUser.AjaxChatUserName;
            }

            int user_number = Business.CurrentApp.AjaxChatUsersW.Count + 1;
            <span class="cpp-comment">// If user may log in </span>
            if (user_number &lt;= MaxLoggedInUsers)
            {
                <span class="cpp-comment">// Get first not used guest user name</span>
                while (Business.CurrentApp.AjaxChatUsersW[<span class="cpp-string">"ChatGuest"</span> + user_number.ToString()] != null)
                {
                    if (user_number &lt; int.MaxValue)
                        user_number++;
                    else
                        return <span class="cpp-string">""</span>;
                }

                <span class="cpp-comment">// Add to writing users list</span>
                Business.CurrentApp.AjaxChatUsersW.Add(<span class="cpp-string">"Guest"</span> + user_number.ToString(), 1);

                <span class="cpp-comment">// Save user name to session</span>
                Business.CurrentUser.AjaxChatUserName = <span class="cpp-string">"Guest"</span> + user_number.ToString();

                UpdateUserLastActivityTime();
            }
            return user_name;
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void ChangeUserName(string tentative_name)
        {
            if (Business.CurrentUser.AjaxChatUserName == <span class="cpp-string">""</span>)
            {
                int user_number = Business.CurrentApp.AjaxChatUsersW.Count + 1;
                <span class="cpp-comment">// If user may not log in inform</span>
                if (user_number &gt; MaxLoggedInUsers)
                    throw new Exception(<span class="cpp-string">"Max. number of writing users reached. You'll be inform when someone leaves."</span>);
            }

            tentative_name = tentative_name.Trim();
            if (tentative_name == <span class="cpp-string">""</span>)
                throw new Exception(<span class="cpp-string">"You must enter a user name to be able to chat."</span>);
            if (tentative_name.IndexOf('-') &gt;= 0)
                throw new Exception(<span class="cpp-string">"Character '-' is not allowed in the user name."</span>);
            if (tentative_name.Length &gt; 10)
                tentative_name = tentative_name.Substring(0, 10);

            <span class="cpp-comment">// If exists inform</span>
            if (Business.CurrentApp.AjaxChatUsersW[tentative_name] != null)
                throw new Exception(<span class="cpp-string">"User name already in use. Please enter another."</span>);

            <span class="cpp-comment">// Remove old user name from the list</span><span class="cpp-comment">// Add to writing users list</span>
            ChangeUserNameInWList(tentative_name);

            <span class="cpp-comment">// Save user name to session</span>
            Business.CurrentUser.AjaxChatUserName = tentative_name;

            UpdateUserLastActivityTime();
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void ChangeUserNameInWList(string new_name)
        {
            <span class="cpp-comment">// If it already had a user name then remove from writing users</span>
            if (Business.CurrentUser.AjaxChatUserName != null &amp;&amp; Business.CurrentUser.AjaxChatUserName != <span class="cpp-string">""</span>)
            {
                Business.CurrentApp.AjaxChatUsersW.Remove(Business.CurrentUser.AjaxChatUserName);

                <span class="cpp-comment">// If it had a saved last activity time remove it</span>
                Business.CurrentApp.AjaxChatUsersWLastActivity.Remove(Business.CurrentUser.AjaxChatUserName);

                <span class="cpp-comment">// Add to writing users list</span>
                Business.CurrentApp.AjaxChatUsersW.Add(new_name, 1);
                <span class="cpp-comment">// NOTE: It is not necessary to add it to the last activity table because it will be added after this</span>
            }
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void Post(string message)
        {
            <span class="cpp-comment">// Cut max message length</span>
            message = message.Trim();
            if (message.Length &gt; MaxMessageLength)
                message = message.Substring(0, MaxMessageLength);

            if (message != <span class="cpp-string">""</span>)
            {
                <span class="cpp-comment">// Add time and nickname to identify message</span>
                string message_head = Business.CurrentUser.AjaxChatUserName + <span class="cpp-string">" says: "</span>;
                message = message_head + message;

                ValidateMessage(Business.CurrentApp.AjaxChat, message);

                <span class="cpp-comment">// Add message id</span>
                message = GetNextId(Business.CurrentApp.AjaxChat) + message;

                <span class="cpp-comment">// Insert new message</span>
                Business.CurrentApp.AjaxChat.Insert(0, message);

                <span class="cpp-comment">// Delete lines after MaxChatLines</span>
                while (Business.CurrentApp.AjaxChat.Count &gt; MaxChatLines)
                    Business.CurrentApp.AjaxChat.RemoveAt(Business.CurrentApp.AjaxChat.Count - 1);

                UpdateUserLastActivityTime();
            }
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void ValidateMessage(ArrayList ajax_chat_list, string message)
        {
            int index;
            <span class="cpp-comment">// Create array list of chat without message numbers</span>
            ArrayList ajax_chat_nonumbers = new ArrayList();

            for (index = 0; index &lt; ajax_chat_list.Count; index++)
                ajax_chat_nonumbers.Add(ajax_chat_list[index].ToString().Split('-')[1]);

            <span class="cpp-comment">// Check for spam</span>
            if (ajax_chat_nonumbers.Contains(message))
                throw new Exception(<span class="cpp-string">"Please no spam..."</span>);

            <span class="cpp-comment">// Create message words array</span>
            string[] m_w = message.Replace(<span class="cpp-string">"\n"</span>, <span class="cpp-string">" "</span>).Replace(<span class="cpp-string">"\r"</span>, <span class="cpp-string">""</span>).Split(' ');
            ArrayList message_words = new ArrayList();
            for (index = 0; index &lt; m_w.Length; index++)
                message_words.Add(m_w[index]);

            <span class="cpp-comment">// Check for bad words</span>
            for (index = 0; index &lt; BadWords.Length; index++)
                if (message_words.Contains(BadWords[index]))
                    throw new Exception(<span class="cpp-string">"Please no bad words..."</span>);
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void UpdateUserLastActivityTime()
        {
            if (Business.CurrentUser.AjaxChatUserName != null &amp;&amp; Business.CurrentUser.AjaxChatUserName != <span class="cpp-string">""</span>)
                UpdateUserLastActivityTime(Business.CurrentApp.AjaxChatUsersWLastActivity);
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static void UpdateUserLastActivityTime(Hashtable ajax_chat_users_w_last_activity)
        {
            if (ajax_chat_users_w_last_activity[Business.CurrentUser.AjaxChatUserName] == null)
                ajax_chat_users_w_last_activity.Add(Business.CurrentUser.AjaxChatUserName, DateTime.Now);
            else
                ajax_chat_users_w_last_activity[Business.CurrentUser.AjaxChatUserName] = DateTime.Now;
        }

        public static DataTable GetUsersDataSource()
        {
            <span class="cpp-comment">// Create data source</span>
            DataTable dtSource = new DataTable();
            dtSource.Columns.Add(new DataColumn(<span class="cpp-string">"ChatUsers"</span>));
            dtSource.Columns.Add(new DataColumn(<span class="cpp-string">"ChatLastActivity"</span>));
            DataRow drSource;
            DateTime last_activity;

            if (Business.CurrentApp.AjaxChatUsersW.Keys.Count &gt; 0)
            {
                <span class="cpp-comment">// Add list of users to datasource</span>
                foreach (object key in Business.CurrentApp.AjaxChatUsersW.Keys)
                {
                    drSource = dtSource.NewRow();
                    drSource[<span class="cpp-string">"ChatUsers"</span>] = key.ToString();
                    <span class="cpp-comment">// Get last activity time</span>
                    if (Business.CurrentApp.AjaxChatUsersWLastActivity[key] != null)
                    {
                        last_activity = (DateTime)Business.CurrentApp.AjaxChatUsersWLastActivity[key];
                        <span class="cpp-comment">// Convert to GMT time</span>
                        Thread.CurrentThread.CurrentCulture = new CultureInfo(<span class="cpp-string">"en-US"</span>);
                        string dateFormat = CultureInfo.InvariantCulture.DateTimeFormat.RFC1123Pattern.Replace(<span class="cpp-string">"'GMT'"</span>, <span class="cpp-string">"zzz"</span>);
                        string dateString = last_activity.ToString(dateFormat);
                        drSource[<span class="cpp-string">"ChatLastActivity"</span>] = dateString;

                    }
                    else
                        drSource[<span class="cpp-string">"ChatLastActivity"</span>] = <span class="cpp-string">"&lt;none&gt;"</span>;
                    dtSource.Rows.Add(drSource);
                }
            }
            else
            {
                drSource = dtSource.NewRow();
                drSource[<span class="cpp-string">"ChatUsers"</span>] = <span class="cpp-string">"&lt;none&gt;"</span>;
                drSource[<span class="cpp-string">"ChatLastActivity"</span>] = <span class="cpp-string">"&lt;none&gt;"</span>;
                dtSource.Rows.Add(drSource);
            }


            <span class="cpp-comment">// Sort by user name</span>
            dtSource.DefaultView.Sort = <span class="cpp-string">"ChatUsers"</span>;

            return dtSource;
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string Read()
        {
            int id;
            int max_id = 0;
            string message;

            <span class="cpp-comment">// Create chat list if not created</span>
            System.Text.StringBuilder chat = new System.Text.StringBuilder();

            <span class="cpp-comment">// Get chat from application level</span>
            ArrayList ajax_chat_list = Business.CurrentApp.AjaxChat;

            <span class="cpp-comment">// Get last read id from user session if already saved</span>
            int last_read_id = Business.CurrentUser.AjaxChatLastReadId;

            <span class="cpp-comment">// Write it to a string</span>
            int index;
            ArrayList chat_arr = new ArrayList();
            for (index = 0; index &lt; ajax_chat_list.Count; index++)
            {
                id = GetId(ajax_chat_list[index].ToString());
                if (index == 0)
                    max_id = id;
                message = GetMessage(ajax_chat_list[index].ToString());

                <span class="cpp-comment">// If last read message read reached then stop reading</span>
                if (id == last_read_id)
                    break;
                <span class="cpp-comment">// Note: Why not "&lt;="?</span><span class="cpp-comment">//       Because "&lt;=" may cause stop of reading</span><span class="cpp-comment">// Example: If last message read is 9, and then the id 10 message was sent</span><span class="cpp-comment">//          and then the id message 1 was sent, first message read would be id 1 that is &lt; than 9</span><span class="cpp-comment">//          and wouldn't be read although it should be</span><span class="cpp-comment">//          (if read, comparing just equal, then id 10 would be read and in 9 it would stop)</span>

                chat_arr.Add(System.Web.HttpUtility.HtmlEncode(message) + <span class="cpp-string">"\n"</span>);
                <span class="cpp-comment">//chat.Append(Server.HtmlEncode(message) + "\n");</span>
            }

            <span class="cpp-comment">// Build inverted order chat string</span>
            for (index = chat_arr.Count - 1; index &gt;= 0; index--)
                chat.Append(chat_arr[index].ToString());

            <span class="cpp-comment">// Set max_id as new last_read_id to user session</span>
            Business.CurrentUser.AjaxChatLastReadId = max_id;

            UpdateUserLastActivityTime();

            <span class="cpp-comment">// Return chat string to user with max_id to set as new last_read_id</span>
            return chat.ToString();
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string GetUsersWriting()
        {
            return Business.CurrentApp.AjaxChatUsersW.Count.ToString();
        }

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string GetUsersReading()
        {
            return GetIntUsersReading().ToString();
        }

<span class="cpp-preprocessor">        #endregion</span>
    }

}


</pre><p></p><p> </p><p>See a working example: <a href="http://spilafis.com.ar/ChatPage.aspx"><font color="#002c99">http://spilafis.com.ar/ChatPage.aspx</font></a></p><p>Or a more complex (same code but enhanced) but better working example: <a href="http://spilafis.com.ar/ChatAjaxNet.aspx"><font color="#002c99">http://spilafis.com.ar/ChatAjaxNet.aspx</font></a></p><p><span style="FONT-WEIGHT: bold; COLOR: red">License and Disclaimer: All the intructions, code, html and everything presented in this page is provided 'as is' with no warranties what so ever.<br />The only condition for you to use this software is that you keep the link to http://www.spilafis.com.ar in the chat page as provided.<br />Please support freeware keeping the link. </span></p><img src ="http://www.cnitblog.com/MartinYao/aggbug/24710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-26 22:53 <a href="http://www.cnitblog.com/MartinYao/articles/24710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DataTable Synchronization Manager</title><link>http://www.cnitblog.com/MartinYao/articles/24709.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 26 Mar 2007 14:49:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24709.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24709.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24709.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24709.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/cs/database/dtsm/dtsm.zip">Download source files - 17.7 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p nd="1">In my <a href="http://www.codeproject.com/cs/database/dttl.asp">previous article</a>, I discussed the <a href="http://www.codeproject.com/cs/database/dttl.asp"><code>DataTableTransactionLog</code></a>. This article further develops those concepts by implementing a <code nd="2">DataTable</code> synchronization manager. The synchronization manager is responsible for taking the transaction records and creating a list of record "packets". It also takes a list of record "packets" and reconstructs the transaction records. These transaction records can then be applied to a target <code nd="3">DataTable</code>, synchronizing the individual <code nd="4">DataTable</code>s.</p>
		<h2>Refactoring</h2>
		<p nd="5">Several refactorings have been made that readers of the previous article should be aware of. Besides the change of namespace, to "<code nd="6">Clifton.Data</code>", the others are the following:</p>
		<p nd="7">
				<i>DataTableTransactionLog.cs</i>:</p>
		<ul>
				<li nd="8">Moved <code nd="9">Apply</code> and <code nd="10">Revert</code> record manipulation to the <code nd="11">DataTableTransactionLog</code> class. 
</li>
				<li nd="12">Moved <code nd="13">RestoreRowFields</code> and <code nd="14">SaveRowFields</code> to the <code nd="15">DataTableTransactionLog</code> class. 
</li>
				<li nd="16">Added indexer to return the transaction record. 
</li>
				<li nd="17">Modified the <code nd="18">Deleting</code> event to search all the previous transactions and save the current row data in any transaction record that references the row about to be deleted. 
</li>
				<li nd="19">The logger now utilizes an internal <code nd="20">DataView</code> to facilitate finding records by primary key(s) during synchronization. </li>
		</ul>
		<p nd="21">
				<i>DataTableTransactionRecord.cs</i>:</p>
		<ul>
				<li nd="23">
						<code nd="22">TransType</code> property renamed to <code nd="24">TransactionType</code>. 
</li>
				<li nd="26">
						<code nd="25">TransactionType</code> enumeration renamed to <code nd="27">RecordType</code>. 
</li>
				<li nd="28">Added a virtual <code nd="29">Apply</code> method that implements the logic that used to be in the <code nd="30">DataTableTransactionLog</code> class. 
</li>
				<li nd="31">Added a non-virtual <code nd="32">Revert</code> method that implements the logic that used to be in the <code nd="33">DataTableTransactionLog</code> class. 
</li>
				<li nd="35">
						<code nd="34">columnValues</code> is exposed (<code nd="36">ColumnValues</code>) and is settable. </li>
		</ul>
		<h2>Architecture</h2>
		<p nd="37">There is a specific architecture that drives the application's interface to the synchronization manager. The application requirements are:</p>
		<ul>
				<li nd="38">ensuring that each <code nd="39">DataTable</code> implements the same column names. 
</li>
				<li nd="40">utilizing one or more primary keys to uniquely identify each row. 
</li>
				<li nd="41">ensuring that primary key values are initialized and valid before getting the transaction packets from the synchronization manager. 
</li>
				<li nd="42">ensuring that primary key values are initialized and valid before applying transaction packets to the target <code nd="43">DataTable</code>. 
</li>
				<li nd="44">the primary keys are determined from the <code nd="45">DataTable</code>'s <code nd="46">PrimaryKey</code> array. </li>
		</ul>
		<p nd="47">The following is a UML diagram illustrating the relationships between the logger and the synchronization manager:</p>
		<p>
				<img height="798" src="http://www.codeproject.com/cs/database/dtsm/uml.png" width="549" border="0" />
		</p>
		<p nd="48">The classes relevant to the <code nd="49">DataTable</code> synchronization will be discussed next.</p>
		<h3>TransactionRecordPacket</h3>
		<p nd="50">In the previous article, the <code nd="51">DataTableTransactionRecord</code> tracked changes to <code nd="52">DataRow</code> instances. Since each <code nd="53">DataTable</code> will have a different <code nd="54">DataRow</code> instance, it is necessary to use a different mechanism to synchronize unique <code nd="55">DataTable</code> instances. The mechanism I chose was to utilize the primary key column(s). If your <code nd="56">DataTable</code> does not have a primary key column, you will have to add one, or modify your query to acquire the primary key column from the persistent store.</p>
		<p nd="57">The <code nd="58">TransactionRecordPacket</code> stores the primary key values in a dictionary, mapping the primary key column name and the primary key value. If the transaction is a column change, the column name of the field being changed and the new value is also stored. The old value is not preserved, nor is the entire row's field values if the row is deleted.</p>
		<h4>Implementation</h4>
		<p nd="59">The critical part of the implementation is the call to <code nd="60">GetGuaranteedRowValue</code>, which gets the specified field value regardless of whether the row has been deleted:</p>
		<pre lang="cs" nd="62">
				<span class="cs-keyword" nd="61">public</span> TransactionRecordPacket(DataTableTransactionRecord record)
{
  pkValues = <span class="cs-keyword" nd="63">new</span> Dictionary&lt;<span class="cs-keyword" nd="64">string</span>, <span class="cs-keyword" nd="65">object</span>&gt;();
  tranType = record.TransactionType;

  <span class="cs-keyword" nd="66">foreach</span> (DataColumn dc <span class="cs-keyword" nd="67">in</span> record.Row.Table.PrimaryKey)
  {
    pkValues.Add(dc.ColumnName, record.GetGuaranteedRowValue(dc.ColumnName));
  }

  <span class="cs-comment" nd="68">// Fill in some additional information if the transaction is a ChangeField.</span><span class="cs-keyword" nd="69">if</span> (tranType == DataTableTransactionRecord.RecordType.ChangeField)
  {
    columnName = record.ColumnName;
    newValue = record.NewValue;
  }
}</pre>
		<p nd="70">This means that even for deleted rows, we can acquire the primary key field values so we can find the row in the <code nd="71">DataTable</code> being synchronized and delete it. The internal implementation of the <code nd="72">GetGuaranteedRowValue</code> method is:</p>
		<pre lang="cs" nd="75">
				<span class="cs-keyword" nd="73">public</span>
				<span class="cs-keyword" nd="74">object</span> GetGuaranteedRowValue(<span class="cs-keyword" nd="76">string</span> fieldName)
{
  <span class="cs-keyword" nd="77">object</span> ret = <span class="cs-keyword" nd="78">null</span>;

  <span class="cs-keyword" nd="79">if</span> (WasDeleted)
  {
    ret = columnValues[fieldName];
  }
  <span class="cs-keyword" nd="80">else</span>
  {
    <span class="cs-keyword" nd="81">if</span> (row.RowState == DataRowState.Deleted)
    {
      <span class="cs-keyword" nd="82">throw</span><span class="cs-keyword" nd="83">new</span> DataTableTransactionException(
          <span class="cpp-string" nd="84">"Row has been deleted and there is no saved column values."</span>);
    }

    ret = row[fieldName];
  }

  <span class="cs-keyword" nd="85">return</span> ret;
}</pre>
		<p nd="86">The <code nd="87">columnValues</code> property is populated when the record is deleted, as mentioned above, for all transaction records that reference the row being deleted. This is the refactoring of the <code nd="88">OnRowDeleting</code> handler:</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="89" preid="2"> Collapse</span>
		</div>
		<pre lang="cs" id="pre2" style="MARGIN-TOP: 0px" nd="92">
				<span class="cs-keyword" nd="90">protected</span>
				<span class="cs-keyword" nd="91">void</span> OnRowDeleting(<span class="cs-keyword" nd="93">object</span> sender, DataRowChangeEventArgs e)
{
  <span class="cs-keyword" nd="94">if</span> (doLogging)
  {
    DataTableTransactionRecord record;
    record = <span class="cs-keyword" nd="95">new</span> DataTableTransactionRecord(transactions.Count, e.Row, 
         DataTableTransactionRecord.RecordType.DeleteRow);
    record.SaveRowFields(e.Row);
    OnTransactionAdding(<span class="cs-keyword" nd="96">new</span> TransactionEventArgs(record));
    transactions.Add(record);
    Dictionary&lt;<span class="cs-keyword" nd="97">string</span>, <span class="cs-keyword" nd="98">object</span>&gt; colVals = record.ColumnValues;

    <span class="cs-comment" nd="99">// Tell all transaction records involving this row to save the row fields.</span><span class="cs-comment" nd="100">// This allows us to access deleted row data in earlier transactions.</span><span class="cs-comment" nd="101">// Alternatively, since the row is deleted, all transactions involving the </span><span class="cs-comment" nd="102">// deleted row could be removed. I'm not sure about this approach though--</span><span class="cs-comment" nd="103">// is it possible for transactions to affect other non-deleted data before</span><span class="cs-comment" nd="104">// the row deleted?</span><span class="cs-keyword" nd="105">for</span> (<span class="cs-keyword" nd="106">int</span> i = <span class="cs-literal" nd="107">0</span>; i &lt; transactions.Count - <span class="cs-literal" nd="108">1</span>; i++)
    {
      <span class="cs-keyword" nd="109">if</span> (transactions[i].Row == e.Row)
      {
        transactions[i].ColumnValues = colVals;
      }
    }

    OnTransactionAdded(<span class="cs-keyword" nd="110">new</span> TransactionEventArgs(record));
  }
}</pre>
		<p nd="111">I'm not particularly thrilled with this implementation simply because it requires iterating through the transaction list. Well, optimizations can be done later, right?</p>
		<h3>DataTablePKTransactionRecord</h3>
		<p nd="112">This class is derived from <code nd="113">DataTableTransactionRecord</code> and overrides the <code nd="114">Apply</code> method. As mentioned above, the primary key values of a row must be used rather than the <code nd="115">DataRow</code> instance.</p>
		<h4>Implementation</h4>
		<p nd="116">The <code nd="117">Apply</code> method is implemented as follows:</p>
		<div class="precollapse" id="premain3" style="WIDTH: 100%">
				<img id="preimg3" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="3" />
				<span id="precollapse3" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="118" preid="3"> Collapse</span>
		</div>
		<pre lang="cs" id="pre3" style="MARGIN-TOP: 0px" nd="121">
				<span class="cs-keyword" nd="119">public</span>
				<span class="cs-keyword" nd="120">override</span> DataRow Apply(DataView dataView)
{
  DataTable sourceTable=dataView.Table;

  <span class="cs-comment" nd="122">// If the transaction record contains a known DataRow, then use</span><span class="cs-comment" nd="123">// the default Apply method.</span><span class="cs-keyword" nd="124">if</span> (row != <span class="cs-keyword" nd="125">null</span>)
  {
    <span class="cs-keyword" nd="126">base</span>.Apply(dataView);
  }
  <span class="cs-keyword" nd="127">else</span>
  {
    <span class="cs-comment" nd="128">// We have to use the PK value information to determine the row.</span><span class="cs-keyword" nd="129">switch</span> (transType)
    { 
      <span class="cs-keyword" nd="130">case</span> RecordType.NewRow:
        row = sourceTable.NewRow();
        SetPKFieldValues();
        sourceTable.Rows.Add(row);
        <span class="cs-keyword" nd="131">break</span>;

      <span class="cs-keyword" nd="132">case</span> RecordType.DeleteRow:
        row = FindRow(dataView);
        row.Delete();
        <span class="cs-keyword" nd="133">break</span>;

     <span class="cs-keyword" nd="134">case</span> RecordType.ChangeField:
       row = FindRow(dataView);
       row[columnName] = newValue;
       <span class="cs-keyword" nd="135">break</span>;
    }
  }

  <span class="cs-keyword" nd="136">return</span><span class="cs-keyword" nd="137">null</span>;
}</pre>
		<p nd="138">The call to <code nd="139">SetPKFieldValues</code>:</p>
		<pre lang="cs" nd="142">
				<span class="cs-keyword" nd="140">protected</span>
				<span class="cs-keyword" nd="141">void</span> SetPKFieldValues()
{
  <span class="cs-keyword" nd="143">foreach</span> (KeyValuePair&lt;<span class="cs-keyword" nd="144">string</span>, <span class="cs-keyword" nd="145">object</span>&gt; kvp <span class="cs-keyword" nd="146">in</span> pkFieldNameValues)
  {
    row[kvp.Key] = kvp.Value;
  }
}</pre>
		<p nd="147">is necessary so that a new row can be located after it has been added. This results in some redundancy in transactions that set the primary key values even though they've already been set here. A possible refactoring would be to accumulate all the field change transactions and apply them all at once when adding a new row. That would involve an implementation that has access to all the transaction records, whereas what I'm focusing on here is applying transaction records one at a time. This is an important step as it validates the implementation for the basic requirements.</p>
		<p nd="148">The <code nd="149">FindRow</code> method sets up the primary key field values as determined by the packet's primary key field/value dictionary:</p>
		<pre lang="cs" nd="151">
				<span class="cs-keyword" nd="150">protected</span> DataRow FindRow(DataView dataView)
{
  <span class="cs-keyword" nd="152">if</span> ((dataView.Sort == <span class="cs-keyword" nd="153">null</span>) || (dataView.Sort == String.Empty))
  {
    <span class="cs-keyword" nd="154">throw</span><span class="cs-keyword" nd="155">new</span> DataTableSynchronizationManagerException(<span class="cpp-string" nd="156">"The transaction 
       logger's SetupSort method must be called before synchronization."</span>);
  }

  <span class="cs-keyword" nd="157">object</span>[] pks = <span class="cs-keyword" nd="158">new</span><span class="cs-keyword" nd="159">object</span>[pkValues.Count];
  pkValues.Values.CopyTo(pks, <span class="cs-literal" nd="160">0</span>);
  <span class="cs-keyword" nd="161">int</span> idx = dataView.Find(pks);

  <span class="cs-keyword">if</span> (idx &lt; <span class="cs-literal">0</span>)
  {
    <span class="cs-keyword">throw</span><span class="cs-keyword">new</span> DataTableSynchronizationManagerException(<span class="cpp-string">"Could not find row 
          to update."</span>);
  }

  <span class="cs-keyword">return</span> dataView[idx].Row;
}</pre>
		<h3>DataTableSynchronizationManager</h3>
		<p>The synchronization manager interfaces with the <code>DataTableTransactionLog</code> instance to acquire the transactions and convert them into <code>TransactionRecordPacket</code> instances. It also does the reverse--taking a collection of <code>TransactionRecordPacket</code> instances and adding them to the logger's transaction collection.</p>
		<h4>Implementation</h4>
		<p>
				<code>GetTransactions</code> returns a list of <code>TransactionRecordPacket</code> instances:</p>
		<pre lang="cs">
				<span class="cs-keyword">public</span> List&lt;TransactionRecordPacket&gt; GetTransactions()
{
  <span class="cs-keyword">if</span> (logger.SourceTable.PrimaryKey == <span class="cs-keyword">null</span>)
  {
    <span class="cs-keyword">throw</span><span class="cs-keyword">new</span> DataTableTransactionException(
       <span class="cpp-string">"GetTransactions requires at least one PK."</span>);
  }

  List&lt;TransactionRecordPacket&gt; packets = <span class="cs-keyword">new</span> List&lt;TransactionRecordPacket&gt;();

  <span class="cs-keyword">foreach</span> (DataTableTransactionRecord record <span class="cs-keyword">in</span> logger.Log)
  {
    TransactionRecordPacket trp = <span class="cs-keyword">new</span> TransactionRecordPacket(record);
    packets.Add(trp);
  }

  <span class="cs-keyword">return</span> packets;
}</pre>
		<p>These can then be applied to a transaction of the logger managing the mirrored <code>DataTable</code>:</p>
		<pre lang="cs">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">void</span> AddTransactions(List&lt;TransactionRecordPacket&gt; transactionPackets)
{
  <span class="cs-keyword">foreach</span> (TransactionRecordPacket trp <span class="cs-keyword">in</span> transactionPackets)
  {
    logger.Log.Add(<span class="cs-keyword">new</span> DataTablePKTransactionRecord(trp));
  }
}</pre>
		<p>Note how the synchronization manager adds the instances of the specialized <code>DataTablePKTransactionRecord</code> class. This is so that the <code>Appy</code> method can be overridden so that primary key values are used to locate rows rather than the <code>DataRow</code> instance itself.</p>
		<p>Lastly, the synchronization manager implements two methods, <code>SetupSort</code> and <code>Sync</code>, the latter of which is used to apply all transactions added to the transaction log. These are expected to be exclusively <code>DataTablePKTransactionRecord</code> instances:</p>
		<pre lang="cs">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">void</span> Sync()
{
  SetupSort();
  logger.SuspendLogging();

  <span class="cs-keyword">foreach</span> (DataTableTransactionRecord record <span class="cs-keyword">in</span> logger.Log)
  {
    <span class="cs-keyword">if</span> (!(record <span class="cs-keyword">is</span> DataTablePKTransactionRecord))
    {
      <span class="cs-keyword">throw</span><span class="cs-keyword">new</span> DataTableSynchronizationManagerException(<span class="cpp-string">"Expected a record 
          of type DataTablePKTransactionRecord."</span>);
    }

    record.Apply(logger.DataView);
  }

  logger.ResumeLogging();
}</pre>
		<p>The <code>SetupSort</code> method will set up the <code>DataView.Sort</code> property so that the <code>DataView.Find</code> method can be used to find rows based on their primary keys. For this to work, the <code>DataColumn</code> instances in the <code>DataTable</code>'s <code>Column</code> collection must be initialized in the same order:</p>
		<pre lang="cs">
				<span class="cs-keyword">protected</span>
				<span class="cs-keyword">void</span> SetupSort()
{
  <span class="cs-keyword">if</span> (logger.SourceTable.PrimaryKey == <span class="cs-keyword">null</span>)
  { 
    <span class="cs-keyword">throw</span><span class="cs-keyword">new</span> DataTableTransactionException(
        <span class="cpp-string">"GetTransactions requires at least one PK."</span>);
  }

  <span class="cs-keyword">string</span> sortBy = String.Empty;
  <span class="cs-keyword">string</span> comma = String.Empty;

  <span class="cs-keyword">foreach</span> (DataColumn dc <span class="cs-keyword">in</span> logger.SourceTable.PrimaryKey)
  {
    sortBy += comma + dc.ColumnName;
    comma = <span class="cpp-string">", "</span>;
  }

  logger.DataView.Sort = sortBy;
}</pre>
		<h2>Unit Tests</h2>
		<p>I've created a unit test for both the core logger functionality and the synchronization manager.</p>
		<p>
				<img height="310" src="http://www.codeproject.com/cs/database/dtsm/dtsmunittests.PNG" width="396" border="0" />
		</p>
		<h3>Transaction Logger</h3>
		<p>The transaction logger unit tests are a sequential set of unit tests, runnable using my <a href="http://www.marcclifton.com/Projects/AdvancedUnitTesting/tabid/102/Default.aspx" target="_blank">Advanced Unit Test</a> engine.</p>
		<div class="precollapse" id="premain10" style="WIDTH: 100%">
				<img id="preimg10" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="10" />
				<span id="precollapse10" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="10"> Collapse</span>
		</div>
		<pre lang="cs" id="pre10" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">using</span> System;
<span class="cs-keyword">using</span> System.Collections.Generic;
<span class="cs-keyword">using</span> System.Data;

<span class="cs-keyword">using</span> Vts.UnitTest;

<span class="cs-keyword">using</span> Clifton.Data;

<span class="cs-keyword">namespace</span> TransactionLoggerUnitTests
{
  [TestFixture]
  [ProcessTest]
  <span class="cs-keyword">public</span><span class="cs-keyword">class</span> LoggerTests
  {
    <span class="cs-keyword">protected</span> DataTable dt;
    <span class="cs-keyword">protected</span> DataTableTransactionLog dttl;
    <span class="cs-keyword">protected</span> DataRow row;

    [TestFixtureSetUp]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> FixtureSetup()
    {
      dt = <span class="cs-keyword">new</span> DataTable();
      dt.Columns.Add(<span class="cs-keyword">new</span> DataColumn(<span class="cpp-string">"LastName"</span>, <span class="cs-keyword">typeof</span>(<span class="cs-keyword">string</span>)));
      dt.Columns.Add(<span class="cs-keyword">new</span> DataColumn(<span class="cpp-string">"FirstName"</span>, <span class="cs-keyword">typeof</span>(<span class="cs-keyword">string</span>)));

      dttl = <span class="cs-keyword">new</span> DataTableTransactionLog();
      dttl.SourceTable = dt;
    }

    [Test, Sequence(<span class="cs-literal">1</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> NewRow()
    {
      row = dt.NewRow();
      Assertion.Assert(dttl.Log.Count == <span class="cs-literal">1</span>, <span class="cpp-string">"Expected one entry."</span>);
      Assertion.Assert(dttl.Log[<span class="cs-literal">0</span>].TransactionType == 
          DataTableTransactionRecord.RecordType.NewRow, 
          <span class="cpp-string">"Expected new row transaction."</span>);
    }

    [Test, Sequence(<span class="cs-literal">2</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> SetFields()
    {
      row[<span class="cpp-string">"LastName"</span>] = <span class="cpp-string">"Clifton"</span>;
      row[<span class="cpp-string">"FirstName"</span>] = <span class="cpp-string">"Marc"</span>;
      dt.Rows.Add(row);
      Assertion.Assert(dttl.Log.Count == <span class="cs-literal">3</span>, <span class="cpp-string">"Expected three entries."</span>);
      Assertion.Assert(dttl.Log[<span class="cs-literal">1</span>].TransactionType == 
           DataTableTransactionRecord.RecordType.ChangeField, 
           <span class="cpp-string">"Expected change field transaction."</span>);
      Assertion.Assert(dttl.Log[<span class="cs-literal">2</span>].TransactionType == 
           DataTableTransactionRecord.RecordType.ChangeField, 
           <span class="cpp-string">"Expected change field transaction."</span>);
      Assertion.Assert(dttl.Log[<span class="cs-literal">1</span>].NewValue.ToString()==<span class="cpp-string">"Clifton"</span>, 
           <span class="cpp-string">"Incorrect new value."</span>);
      Assertion.Assert(dttl.Log[<span class="cs-literal">2</span>].NewValue.ToString() == <span class="cpp-string">"Marc"</span>, 
           <span class="cpp-string">"Incorrect new value."</span>);
    }

    [Test, Sequence(<span class="cs-literal">3</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> CollectNothing()
    {
      dttl.CollectUncommittedRows();    
      Assertion.Assert(dt.Rows.Count == <span class="cs-literal">1</span>, <span class="cpp-string">"Committed row was collected!"</span>);
    }

    [Test, Sequence(<span class="cs-literal">4</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> CollectUncommitted()
    {
      dt.NewRow();
      dttl.CollectUncommittedRows();
      Assertion.Assert(dttl.Log.Count == <span class="cs-literal">3</span>, <span class="cpp-string">"Expected three entries."</span>);
    }

    [Test, Sequence(<span class="cs-literal">5</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> RevertFirstNameChange()
    {
      dttl.Revert(<span class="cs-literal">2</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>].ToString() ==
         <span class="cpp-string">"Clifton"</span>, <span class="cpp-string">"Incorrect value."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>]==DBNull.Value, 
         <span class="cpp-string">"Incorrect new value."</span>);
    }

    [Test, Sequence(<span class="cs-literal">6</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> RevertLastNameChange()
    {
      dttl.Revert(<span class="cs-literal">1</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>] == DBNull.Value, 
          <span class="cpp-string">"Incorrect value."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>] == DBNull.Value, 
          <span class="cpp-string">"Incorrect new value."</span>);
    }

    [Test, Sequence(<span class="cs-literal">7</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> RevertNewRowChange()
    {
      dttl.Revert(<span class="cs-literal">0</span>);
      Assertion.Assert(dt.Rows.Count == <span class="cs-literal">0</span>, <span class="cpp-string">"Row should have been deleted."</span>);
    }

    [Test, Sequence(<span class="cs-literal">8</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> ApplyNewRow()
    {
      dttl.Apply(<span class="cs-literal">0</span>);
      Assertion.Assert(dt.Rows.Count == <span class="cs-literal">1</span>, <span class="cpp-string">"Row was not added."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>] == DBNull.Value, 
           <span class="cpp-string">"Incorrect value."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>] == DBNull.Value, 
           <span class="cpp-string">"Incorrect new value."</span>);
    }

    [Test, Sequence(<span class="cs-literal">9</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> ApplyLastName()
    {
      dttl.Apply(<span class="cs-literal">1</span>);
      Assertion.Assert(dt.Rows.Count == <span class="cs-literal">1</span>, <span class="cpp-string">"Row was not added."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>].ToString() == <span class="cpp-string">"Clifton"</span>, 
           <span class="cpp-string">"Incorrect value."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>] == DBNull.Value, 
           <span class="cpp-string">"Incorrect new value."</span>);
    }

    [Test, Sequence(<span class="cs-literal">10</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> ApplyFirstName()
    {
      dttl.Apply(<span class="cs-literal">2</span>);
      Assertion.Assert(dt.Rows.Count == <span class="cs-literal">1</span>, <span class="cpp-string">"Row was not added."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>].ToString() == <span class="cpp-string">"Clifton"</span>, 
           <span class="cpp-string">"Incorrect value."</span>);
      Assertion.Assert(dt.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>].ToString() == <span class="cpp-string">"Marc"</span>, 
           <span class="cpp-string">"Incorrect new value."</span>);
    }
  }
}</pre>
		<h3>Synchronization Manager</h3>
		<p>The unit test for the synchronization manager illustrates the basic process for synchronizing two <code>DataTable</code> instances:</p>
		<div class="precollapse" id="premain11" style="WIDTH: 100%">
				<img id="preimg11" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="11" />
				<span id="precollapse11" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="11"> Collapse</span>
		</div>
		<pre lang="cs" id="pre11" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">using</span> System;
<span class="cs-keyword">using</span> System.Collections.Generic;
<span class="cs-keyword">using</span> System.Data;

<span class="cs-keyword">using</span> Vts.UnitTest;

<span class="cs-keyword">using</span> Clifton.Data;

<span class="cs-keyword">namespace</span> TransactionSyncrhonizationManagerUnitTests
{
  [TestFixture]
  [ProcessTest]
  <span class="cs-keyword">public</span><span class="cs-keyword">class</span> CurrencyTests
  {
    <span class="cs-keyword">protected</span> DataTable dt1;
    <span class="cs-keyword">protected</span> DataTableTransactionLog dttl1;
    <span class="cs-keyword">protected</span> DataTable dt2;
    <span class="cs-keyword">protected</span> DataTableTransactionLog dttl2;

    [TestFixtureSetUp]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> FixtureSetup()
    {
      dt1 = <span class="cs-keyword">new</span> DataTable();
      dt1.Columns.Add(<span class="cs-keyword">new</span> DataColumn(<span class="cpp-string">"PK"</span>, <span class="cs-keyword">typeof</span>(Guid)));
      dt1.Columns.Add(<span class="cs-keyword">new</span> DataColumn(<span class="cpp-string">"LastName"</span>, <span class="cs-keyword">typeof</span>(<span class="cs-keyword">string</span>)));
      dt1.Columns.Add(<span class="cs-keyword">new</span> DataColumn(<span class="cpp-string">"FirstName"</span>, <span class="cs-keyword">typeof</span>(<span class="cs-keyword">string</span>)));
      dt1.PrimaryKey = <span class="cs-keyword">new</span> DataColumn[] { dt1.Columns[<span class="cpp-string">"PK"</span>] };

      dttl1 = <span class="cs-keyword">new</span> DataTableTransactionLog(dt1);

      dt2 = dt1.Clone();
      dttl2 = <span class="cs-keyword">new</span> DataTableTransactionLog(dt2);

      DataRow row=dt1.NewRow();
      row[<span class="cpp-string">"PK"</span>]=Guid.NewGuid();
      row[<span class="cpp-string">"LastName"</span>]=<span class="cpp-string">"Clifton"</span>;
      row[<span class="cpp-string">"FirstName"</span>]=<span class="cpp-string">"Marc"</span>;
      dt1.Rows.Add(row);

      row=dt1.NewRow();
      row[<span class="cpp-string">"PK"</span>]=Guid.NewGuid();
      row[<span class="cpp-string">"LastName"</span>]=<span class="cpp-string">"Linder"</span>;
      row[<span class="cpp-string">"FirstName"</span>]=<span class="cpp-string">"Karen"</span>;
      dt1.Rows.Add(row);

      row=dt1.NewRow();
      row[<span class="cpp-string">"PK"</span>]=Guid.NewGuid();
      row[<span class="cpp-string">"LastName"</span>]=<span class="cpp-string">"Doe"</span>;
      row[<span class="cpp-string">"FirstName"</span>]=<span class="cpp-string">"John"</span>;
      dt1.Rows.Add(row);

      dt1.Rows[<span class="cs-literal">2</span>].Delete();
    }

    [Test, Sequence(<span class="cs-literal">1</span>)]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> UpdateMirror()
    {
      DataTableSynchronizationManager dtcm1 = 
          <span class="cs-keyword">new</span> DataTableSynchronizationManager(dttl1);
      List&lt;TransactionRecordPacket&gt; trpList = dtcm1.GetTransactions();

      DataTableSynchronizationManager dtcm2 = 
          <span class="cs-keyword">new</span> DataTableSynchronizationManager(dttl2);
      dtcm2.AddTransactions(trpList);
      dtcm2.Sync();

      Assertion.Assert(dt2.Rows.Count == <span class="cs-literal">2</span>, <span class="cpp-string">"Expected 2 rows."</span>);
      Assertion.Assert(dt2.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"LastName"</span>].ToString() == <span class="cpp-string">"Clifton"</span>, 
          <span class="cpp-string">"Unexpected value"</span>);
      Assertion.Assert(dt2.Rows[<span class="cs-literal">0</span>][<span class="cpp-string">"FirstName"</span>].ToString() == <span class="cpp-string">"Marc"</span>, 
          <span class="cpp-string">"Unexpected value"</span>);      
      Assertion.Assert(dt2.Rows[<span class="cs-literal">1</span>][<span class="cpp-string">"LastName"</span>].ToString() == <span class="cpp-string">"Linder"</span>, 
          <span class="cpp-string">"Unexpected value"</span>);
      Assertion.Assert(dt2.Rows[<span class="cs-literal">1</span>][<span class="cpp-string">"FirstName"</span>].ToString() == <span class="cpp-string">"Karen"</span>, 
          <span class="cpp-string">"Unexpected value"</span>);
    }
  }
}</pre>
		<p>You will note how both the <code>DataTable</code> instances are set up with the same column structure. The transaction packet collection, acquired from the <code>DataTableSynchronizationManager</code> that interfaces to the first logger, is passed to a second <code>DataTableSynchronizationManager</code> instance that interfaces with the second logger. The packets are added and the <code>Sync</code> method is called. Also note that because the two tables are intended to be synchronized, both implement a primary key column now.</p>
		<h2>Conclusion</h2>
		<p>The <code>DataTableSynchronizationManager</code> is a useful component in synchronizing the data between two remote <code>DataTable</code> instances. The transaction packet list is suitable for serialization, using my <a href="http://www.marcclifton.com/Articles/Performance/RawSerializer/tabid/130/Default.aspx" target="_blank">RawSerialization</a> library, which would provide for a compact format to send across a network.</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-26 22:49 <a href="http://www.cnitblog.com/MartinYao/articles/24709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Multi User Chat Room Using ASP.NET 2.0 and AJAX</title><link>http://www.cnitblog.com/MartinYao/articles/24708.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 26 Mar 2007 14:46:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24708.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24708.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24708.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24708.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24708.html</trackback:ping><description><![CDATA[
		<p>
				<br />&lt;UL class=download&gt;<br />&lt;LI&gt;&lt;A href="ASPNetChat/ASPNETCHAT.zip"&gt;Download source files - 10.4 KB&lt;/A&gt; <br />&lt;/LI&gt;&lt;/UL&gt;&lt;IMG height=276 alt="Screenshot - chat.jpg" src="ASPNetChat/chat.jpg" <br />width=582&gt; <br />&lt;H2&gt;Introduction&lt;/H2&gt;<br />&lt;P nd="1"&gt;Building an interactive chat room requires keeping the users up to <br />date with the messages and status of other users. Using ASP.NET AJAX to <br />implement the chat room will remove the unnecessary post backs and will provide <br />a seamless chat experience to the user. &lt;/P&gt;<br />&lt;H2&gt;Background&lt;/H2&gt;<br />&lt;P nd="2"&gt;You can read &lt;A href="AliAspNetChat.asp"&gt;my previous article&lt;/A&gt; on <br />how to build a one to one chat room, this article was built using ASP.NET 1.1 <br />and a third party AJAX library. Most of the Chatting Logic (rooms, users, <br />messages sending, etc..) is used here. &lt;/P&gt;<br />&lt;H2&gt;Code Walkthrough&lt;/H2&gt;<br />&lt;P nd="3"&gt;The App_Code folder contains all the classes that will be used to <br />create our chat application. Let us start with the &lt;CODE nd="4"&gt;ChatUser&lt;/CODE&gt; <br />&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain0 style="WIDTH: 100%"&gt;&lt;IMG id=preimg0 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="0"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre0 style="MARGIN-TOP: 0px" nd="7"&gt;//ChatUser.cs<br />public class ChatUser:IDisposable<br />{<br />    #region Members<br />    public string UserID;<br />    public string UserName;<br />    public bool IsActive;<br />    public DateTime LastSeen;<br />    public int LastMessageReceived;<br />    #endregion </p>
		<p>    #region Constructors<br />    public ChatUser(string id,string userName)<br />    {<br />        this.UserID=id;<br />        this.IsActive=false;<br />        this.LastSeen=DateTime.MinValue ;<br />        this.UserName=userName;<br />        this.LastMessageReceived=0;<br />    }<br />    #endregion </p>
		<p>    #region IDisposable Members<br />    public void Dispose()<br />    {<br />        this.UserID="";<br />        this.IsActive=false;<br />        this.LastSeen=DateTime.MinValue ;<br />        this.UserName="";<br />        this.LastMessageReceived=0;<br />    }<br />    #endregion<br />}<br />&lt;/PRE&gt;<br />&lt;P nd="16"&gt;This class represents the user that will join the chat room. The <br />properties we are interested in are &lt;CODE nd="17"&gt;IsActive&lt;/CODE&gt;, &lt;CODE <br />nd="18"&gt;LastSeen &lt;/CODE&gt;and &lt;CODE nd="19"&gt;LastMessageReceived&lt;/CODE&gt;. Note that <br />in your application you may be using the ASP.NET membership providers. You can <br />easily use this with our chat application. You will find that the &lt;CODE <br />nd="20"&gt;MembershipUser&lt;/CODE&gt; class contains two properties &lt;CODE <br />nd="21"&gt;IsOnline&lt;/CODE&gt; and &lt;CODE nd="22"&gt;LastActivityDate&lt;/CODE&gt; that can be <br />used to replace the properties &lt;CODE nd="23"&gt;IsActive&lt;/CODE&gt; and &lt;CODE <br />nd="24"&gt;LastSeen&lt;/CODE&gt; respectively. &lt;/P&gt;<br />&lt;P nd="25"&gt;Each message that appears in the chat room is represented by the <br />following class:&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain1 style="WIDTH: 100%"&gt;&lt;IMG id=preimg1 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="1"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre1 style="MARGIN-TOP: 0px" nd="28"&gt;//ChatMessage.cs<br />public class Message <br />{<br />    #region Members<br />    public string user;<br />    public string msg;<br />    public MsgType type;<br />    #endregion </p>
		<p>    #region Constructors<br />    public Message(string _user, string _msg, MsgType _type) <br />    {<br />        user = _user;<br />        msg = _msg;<br />        type = _type;<br />    }<br />    public Message(string _user, MsgType _type) : <br />                this(_user, "", _type) { }<br />    public Message(MsgType _type) : this("", "", _type) { }<br />    #endregion </p>
		<p>    #region Methods<br />    public override string ToString()<br />    {<br />        switch(this.type)<br />        {<br />            case MsgType.Msg:<br />            return this.user+" says: "+this.msg;<br />            case MsgType.Join :<br />            return this.user + " has joined the room";<br />            case MsgType.Left :<br />            return this.user + " has left the room";<br />        }<br />        return "";<br />    }<br />    #endregion <br />}</p>
		<p>public enum MsgType { Msg, Start, Join, Left, Action}<br />&lt;/PRE&gt;<br />&lt;P nd="42"&gt;Each chat room contains a hashtable of users and a list of <br />messages.&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain2 style="WIDTH: 100%"&gt;&lt;IMG id=preimg2 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="2"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre2 style="MARGIN-TOP: 0px" nd="45"&gt;//ChatRoom.cs<br />public class ChatRoom : IDisposable<br />{<br />    #region Members<br />    <br />    public List&lt;message /&gt; messages = null;<br />    public string RoomID;<br />    private Dictionary&lt;string,chatuser /&gt; RoomUsers;<br />    private int userChatRoomSessionTimeout;<br />        <br />    #endregion<br />    #region IDisposable Members<br />    public void Dispose()<br />    {<br />        this.messages.Clear();<br />        this.RoomID="";<br />        foreach(object key in RoomUsers.Keys)<br />        {<br />            this.RoomUsers[key.ToString()].Dispose ();<br />        }<br />    }<br />    #endregion<br />        <br />    #region Constructors<br />    public ChatRoom(string roomID) <br />    {<br />        this.messages = new List&lt;message /&gt;();<br />        this.RoomID=roomID;<br />    userChatRoomSessionTimeout = Int32.Parse<br />    (System.Configuration.ConfigurationManager.AppSettings<br />            ["UserChatRoomSessionTimeout"]);<br />        RoomUsers = new Dictionary&lt;string,chatuser /&gt;<br />    (Int32.Parse(System.Configuration.ConfigurationManager.AppSettings<br />                    ["ChatRoomMaxUsers"]));<br />    }<br />    #endregion<br />    .<br />    .<br />    .<br />    }<br />&lt;/PRE&gt;<br />&lt;P nd="55"&gt;The main methods in the &lt;CODE nd="56"&gt;ChatRoom&lt;/CODE&gt; class are <br />sending a message, joining a room and leaving a room. &lt;/P&gt;<br />&lt;DIV class=precollapse id=premain3 style="WIDTH: 100%"&gt;&lt;IMG id=preimg3 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="3"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre3 style="MARGIN-TOP: 0px" nd="61"&gt;//ChatRoom.cs<br />#region Operations Join,Send,Leave<br />    /// Marks the user as inactive<br />    public void LeaveRoom(string userID)<br />    {<br />        //deactivate user <br />        ChatUser user=this.GetUser(userID);<br />        if (user == null)<br />        return ;<br />        user.IsActive=false;<br />        user.LastSeen=DateTime.Now;<br />            this.RoomUsers.Remove(userID);</p>
		<p>        //Add leaving message<br />        Message msg = new Message(user.UserName ,"",MsgType.Left);<br />        this.AddMsg(msg);<br />            <br />    if (IsEmpty())<br />    ChatEngine.DeleteRoom(this.RoomID);<br />    }</p>
		<p>    /// Activates the user and adds a join message to the room<br />    /// &lt;returns /&gt;All the messages sent in the room&lt;/returns /&gt;<br />    public string JoinRoom(string userID,string userName)<br />    {<br />        //activate user <br />        ChatUser user=new ChatUser(userID,userName);<br />        user.IsActive=true;<br />        user.UserName=userName;<br />        user.LastSeen=DateTime.Now;<br />        if (!this.RoomUsers.ContainsKey(userID))<br />        {<br />            //Add join message<br />        Message msg=new Message(user.UserName ,"",MsgType.Join);<br />            this.AddMsg(msg);<br />            //Get all the messages to the user<br />            int lastMsgID;<br />            List&lt;message /&gt; previousMessages=<br />                this.GetMessagesSince(-1,out lastMsgID);<br />            user.LastMessageReceived=lastMsgID;<br />            //return the messages to the user<br />            string str=GenerateMessagesString(previousMessages);<br />            this.RoomUsers.Add(userID,user);<br />            return str;<br />        }<br />        return "";<br />    }</p>
		<p>    /// Adds a message in the room<br />    /// &lt;returns /&gt;All the messages sent from the other user from the last time <br />    ///the user sent a message&lt;/returns /&gt;<br />    public string SendMessage(string strMsg,string senderID)<br />    {<br />        ChatUser user=this.GetUser(senderID);<br />        Message msg=new Message(user.UserName ,strMsg,MsgType.Msg);<br />        user.LastSeen=DateTime.Now;<br />        this.ExpireUsers(userChatRoomSessionTimeout);<br />        this.AddMsg(msg);<br />        int lastMsgID;<br />        List&lt;message /&gt; previousMsgs= this.GetMessagesSince<br />            ( user.LastMessageReceived,out lastMsgID);<br />        if (lastMsgID!=-1)<br />            user.LastMessageReceived=lastMsgID;<br />        string res=this.GenerateMessagesString(previousMsgs);<br />        return res;<br />    }</p>
		<p>    /// Removes the users that hasn't sent any message during the <br />    /// last window seconds <br />    /// &lt;PARAM name="window" /&gt;time in secondes&lt;/PARAM /&gt;<br />    public void ExpireUsers(int window) <br />    {<br />        lock(this) <br />        {<br />    foreach (object key in RoomUsers.Keys)<br />    {<br />            ChatUser usr = this.RoomUsers[key.ToString()];<br />                lock (usr)<br />                {<br />                  if (usr.LastSeen != System.DateTime.MinValue)<br />                  {<br />                    TimeSpan span = DateTime.Now - usr.LastSeen;<br />                    if (span.TotalSeconds &gt; window &amp;&amp; usr.IsActive != false)<br />                        {<br />                          this.LeaveRoom(usr.UserID);<br />                        }<br />                   }<br />                 }<br />                }<br />    }<br />    #endregion <br /> &lt;/PRE&gt;<br />&lt;P nd="80"&gt;To keep the user updated, the following function retrieves all the <br />messages sent in the room up to the last message that was received by the <br />user.&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain4 style="WIDTH: 100%"&gt;&lt;IMG id=preimg4 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="4"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre4 style="MARGIN-TOP: 0px" nd="83"&gt;//ChatRoom.cs<br />&lt;summary /&gt;    /// Returns all the messages sent since the last message <br />    /// the user received<br />    public string UpdateUser(string userID)<br />    {<br />        ChatUser user=this.GetUser(userID);<br />        user.LastSeen=DateTime.Now;<br />        this.ExpireUsers(userChatRoomSessionTimeout);<br />        int lastMsgID;<br />        List&lt;message /&gt; previousMsgs= this.GetMessagesSince<br />            ( user.LastMessageReceived,out lastMsgID);<br />        if (lastMsgID!=-1)<br />            user.LastMessageReceived=lastMsgID;<br />        string res=this.GenerateMessagesString(previousMsgs);<br />        return res;<br />    }<br />    /// Returns a list that contains all messages sent after the <br />    ///             message with id=msgid<br />    /// &lt;PARAM name="msgid" /&gt;The id of the message after which all <br />    ///            the message will be retuned &lt;/PARAM /&gt;<br />    /// &lt;PARAM name="lastMsgID" /&gt;the id of the last message returned&lt;/PARAM /&gt;<br />    public List&lt;message /&gt; GetMessagesSince(int msgid,out int lastMsgID) <br />    {<br />        lock(messages) <br />        {<br />        if ((messages.Count) &lt;= (msgid+1))<br />            lastMsgID=-1;<br />        else<br />            lastMsgID=messages.Count-1;<br />        return messages.GetRange(msgid+1 , messages.Count - (msgid+1));<br />        }<br />    }<br />    <br />&lt;/PRE&gt;<br />&lt;P nd="91"&gt;The &lt;CODE nd="92"&gt;ChatEngine&lt;/CODE&gt; class acts as the container of <br />the chat rooms.&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain5 style="WIDTH: 100%"&gt;&lt;IMG id=preimg5 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="5"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre5 style="MARGIN-TOP: 0px" nd="95"&gt;//ChatEngine.cs<br />    public static class ChatEngine<br />    {<br />        #region Members<br />        private static Dictionary&lt;string, /&gt; Rooms = <br />                new Dictionary&lt;string, /&gt;<br />        (Int32.Parse(System.Configuration.ConfigurationManager.AppSettings<br />                            ["MaxChatRooms"]));<br />        private static int userChatRoomSessionTimeout = <br />        Int32.Parse(System.Configuration.ConfigurationManager.AppSettings<br />                    ["UserChatRoomSessionTimeout"]);<br />        #endregion <br />                <br />        #region Methods<br />    /// Cleans all the chat roomsDeletes the empty chat rooms<br />    public static void CleanChatRooms(object state)<br />        {<br />            lock (Rooms) <br />            {<br />                foreach (object key in Rooms.Keys)<br />                {<br />                    ChatRoom room = Rooms[key.ToString()];<br />                    room.ExpireUsers(userChatRoomSessionTimeout);<br />                    if (room.IsEmpty())<br />                    {<br />                        room.Dispose();<br />                        Rooms.Remove(key.ToString());<br />                    }<br />                }<br />            }<br />        }</p>
		<p>    /// Returns the chat room for this two users or create a new one <br />    /// if nothing exists<br />    public static ChatRoom GetRoom(string roomID)<br />    {<br />        ChatRoom room=null;<br />            lock (Rooms)<br />            {<br />                if (Rooms.ContainsKey (roomID))<br />                    room = Rooms[roomID];<br />                else<br />                {<br />                    room = new ChatRoom(roomID);<br />                    Rooms.Add(roomID, room);<br />                }<br />            }<br />            return room;<br />        }</p>
		<p>    /// Deletes the specified room<br />    public static void DeleteRoom(string roomID)<br />    {<br />            if (!Rooms.ContainsKey(roomID))<br />                return;<br />            lock (Rooms)<br />            {<br />                ChatRoom room = Rooms[roomID];<br />                room.Dispose();<br />                Rooms.Remove(roomID);<br />            }<br />        }<br />        #endregion<br />    }<br />&lt;/PRE&gt;<br />&lt;P nd="106"&gt;An application level timer is set when the application starts to <br />periodically clean the empty chat rooms.&lt;/P&gt;&lt;PRE lang=cs nd="108"&gt;//Global.asax<br />void Application_Start(object sender, EventArgs e) <br />    {<br />        System.Threading.Timer ChatRoomsCleanerTimer = <br />        new System.Threading.Timer(new TimerCallback<br />        (ChatEngine.CleanChatRooms), null, 1200000, 1200000);<br />    }<br />&lt;/PRE&gt;<br />&lt;P nd="109"&gt;When you run the application, it will take you to the default.aspx <br />page, where you are prompted to enter the user name that you will use in the <br />chat room. After that the user chooses the chat room that he will join.&lt;/P&gt;<br />&lt;P nd="110"&gt;The chatting page consists of a text area that displays all the <br />messages in the chat room and a list box that shows all the online users in the <br />room. The user writes his message in the text box and presses enter or clicks <br />the send button to send the message. A text box looks like this:&lt;/P&gt;<br />&lt;P&gt;&lt;IMG height=276 alt="Chat Room" src="ASPNetChat/chat.jpg" width=582 <br />border=0&gt;&lt;/P&gt;<br />&lt;H2&gt;Client Side AJAX &lt;/H2&gt;<br />&lt;P nd="111"&gt;The Chat.aspx page contains javascript that calls methods on the &lt;A <br />class=iAs <br />style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" <br />href="#" target=_blank itxtdid="3442207"&gt;server&lt;/A&gt; using AJAX. To call a method <br />on the server asynchoronously in AJAX you can: &lt;/P&gt;<br />&lt;UL&gt;<br />&lt;LI nd="112"&gt;Either place this method inside a web service and apply the &lt;CODE <br />nd="113"&gt;ScriptService&lt;/CODE&gt; attribute to this web service <br />&lt;LI nd="114"&gt;Or place this method in the aspx page as a static public method and <br />apply the &lt;CODE nd="115"&gt;WebMethod&lt;/CODE&gt; attribute to it &lt;/LI&gt;&lt;/UL&gt;<br />&lt;P nd="116"&gt;I used the second approach in calling the server side methods.&lt;/P&gt;<br />&lt;P nd="117"&gt;All the javascript code is placed in the scripts.js file, there are <br />four asynchronous requests that are made from the javascript to the server, the <br />first two are sent periodically using a javascript timer, the &lt;CODE <br />nd="118"&gt;updateUser&lt;/CODE&gt; request is used to get all the messages that were <br />sent by the other users and updates the text area with these messages. The &lt;CODE <br />nd="119"&gt;updateMembers&lt;/CODE&gt; request is used to get all the online members in <br />the room and updates the members list box such that the members that left are <br />removed from the list and the newly joined members are added to the list. The <br />javascript code for these two requests is shown below:&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain7 style="WIDTH: 100%"&gt;&lt;IMG id=preimg7 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="7"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=jscript id=pre7 style="MARGIN-TOP: 0px" nd="121"&gt;//scripts.js<br />        var msgTimer = "";<br />           var membersTimer = "";<br />        <br />        startTimers();<br />      <br />        function startTimers()<br />        {<br />            msgTimer = window.setInterval("updateUser()",3000);<br />            membersTimer = window.setInterval("updateMembers()",10000);<br />        } <br />        function updateUser()<br />        {<br />            PageMethods.UpdateUser($get("hdnRoomID").value, UpdateMessages);<br />        }<br />        function updateMembers()<br />        {<br />            PageMethods.UpdateRoomMembers($get("hdnRoomID").value, <br />                            UpdateMembersList);<br />        }<br />        function UpdateMessages(result)<br />        {<br />            $get("txt").value=$get("txt").value+result;<br />            $get("txt").doScroll();<br />        }<br />        function UpdateMembersList(result)<br />        {<br />           // alert(result);<br />            var users=result.split(",");<br />           // alert(users.length);<br />            var i=0;<br />                                <br />                $get("lstMembers").options.length=0;<br />                 var i=0;<br />                while (i &lt; users.length)<br />                {<br />                    if (users[i]!="");<br />                    {<br />                        var op=new Option(users[i],users[i]);<br />                        $get("lstMembers").options[$get("lstMembers").<br />                    options.length]= op;<br />                    }<br />                    i+=1;<br />                }<br />               }<br />&lt;/PRE&gt;<br />&lt;P nd="136"&gt;The &lt;CODE nd="137"&gt;PageMethods&lt;/CODE&gt; class is generated by the AJAX <br />script manager control and provides a proxy for all your server methods so that <br />you can call the method by passing it your parameters and passing the name of <br />the script callback function (the function that will be called when the result <br />of your method arrives from the server).&lt;/P&gt;<br />&lt;P nd="138"&gt;The corresponding server side methods for these two requests are <br />shown below:&lt;/P&gt;<br />&lt;DIV class=precollapse id=premain8 style="WIDTH: 100%"&gt;&lt;IMG id=preimg8 <br />style="CURSOR: hand" height=9 src="<a href="http://www.codeproject.com/images/minus.gif">http://www.codeproject.com/images/minus.gif</a>" <br />width=9 preid="8"&gt; Collapse&lt;/DIV&gt;&lt;PRE lang=cs id=pre8 style="MARGIN-TOP: 0px" nd="143"&gt;//Chat.aspx.cs<br />  <br />    /// This function is called peridically called from the user to update <br />    /// the messages<br />    [WebMethod]<br />    static public string UpdateUser(string roomID)<br />    {<br />        try<br />        {<br />            ChatRoom room = ChatEngine.GetRoom(roomID);<br />            if (room != null)<br />            {<br />                string res = "";<br />                if (room != null)<br />                {<br />                    res = room.UpdateUser(HttpContext.Current.Session<br />                    ["UserName"].ToString());<br />                }<br />                return res;<br />            }<br />        }<br />        catch (Exception ex)<br />        {</p>
		<p>        }<br />        return "";<br />    }<br />    /// Returns a comma separated string containing the names of the users <br />    /// currently online<br />    [WebMethod]<br />    static public string UpdateRoomMembers(string roomID)<br />    {<br />        try<br />        {<br />            ChatRoom room = ChatEngine.GetRoom(roomID);<br />            if (room != null)<br />            {<br />                IEnumerable&lt;string /&gt; users=room.GetRoomUsersNames ();<br />                string res="";</p>
		<p>                foreach (string  s in users)<br />                {<br />                    res+=s+",";<br />                }<br />                return res;<br />            }<br />        }<br />        catch (Exception ex)<br />        {<br />        }<br />        return "";<br />    }<br />&lt;/PRE&gt;<br />&lt;P nd="152"&gt;The third request is sent when the user presses the send button. <br />This request sends the user message to the room.&lt;/P&gt;&lt;PRE lang=jscript nd="153"&gt;//scripts.js<br />    function button_clicked()<br />    {<br />        PageMethods.SendMessage($get("txtMsg").value,$get<br />        ("hdnRoomID").value, UpdateMessages, errorCallback);<br />        $get("txtMsg").value="";<br />        $get("txt").scrollIntoView("true");<br />    }<br />    <br />    function errorCallback(result)<br />    {<br />        alert("An error occurred while invoking the remote method: " <br />        + result);<br />    }<br />&lt;/PRE&gt;<br />&lt;P nd="161"&gt;The corresponding server side method for this request is:&lt;/P&gt;&lt;PRE lang=cs&gt;//Chat.aspx.cs<br />&lt;summary /&gt;    /// This function is called from the client script <br />    [WebMethod]<br />    static public string SendMessage(string msg, string roomID)<br />    {<br />        try<br />        {<br />            ChatRoom room = ChatEngine.GetRoom(roomID);<br />            string res = "";<br />            if (room != null)<br />            {<br />                res = room.SendMessage<br />         (msg, HttpContext.Current.Session["UserName"].ToString());<br />            }<br />            return res;<br />        }<br />        catch (Exception ex)<br />        {</p>
		<p>        }<br />        return "";<br />    }<br />&lt;/PRE&gt;<br />&lt;P&gt;The last request is called during the &lt;CODE&gt;onunload&lt;/CODE&gt; event of the <br />&lt;body&gt; element. This method notifies the server that the user left the <br />room. &lt;/P&gt;&lt;PRE lang=jscript&gt;//scripts.js<br />    function Leave()<br />    {<br />        stopTimers();<br />        PageMethods.LeaveRoom($get("hdnRoomID").value);<br />    }<br />&lt;/PRE&gt;<br />&lt;P&gt;The corresponding server side method for this request is:&lt;/P&gt;&lt;PRE lang=cs&gt;//Chat.aspx.cs<br />&lt;summary /&gt;    /// This function is called from the client when the user is about to <br />    /// leave the room<br />    [WebMethod]<br />    static public string LeaveRoom(string roomID)<br />    {<br />        try<br />        {<br />            ChatRoom room = ChatEngine.GetRoom(roomID);<br />            if (room != null)<br />                room.LeaveRoom(HttpContext.Current.Session["UserName"].<br />                            ToString());<br />        }<br />        catch (Exception ex)<br />        {</p>
		<p>        }<br />        return "";<br />    }<br />&lt;/PRE&gt;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24708.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-26 22:46 <a href="http://www.cnitblog.com/MartinYao/articles/24708.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rich Text Editor </title><link>http://www.cnitblog.com/MartinYao/articles/24400.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 21 Mar 2007 15:42:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24400.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24400.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24400.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24400.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24400.html</trackback:ping><description><![CDATA[
		<table class="articleViewer">
				<tbody>
						<tr>
								<td class="abstract">
										<span id="abstract">
										</span>
								</td>
								<td class="photo">
										<a id="authorLink" href="http://aspalliance.com/author.aspx?uId=65748">
										</a>
								</td>
						</tr>
						<tr>
								<td class="author" colspan="2">
								</td>
						</tr>
						<tr>
								<td class="ratingBar">
										<span id="ArticleViewsLabel">
										</span>
								</td>
						</tr>
						<tr>
								<td colspan="2">
										<div style="FLOAT: left">
												<span id="lblArticleContents">
														<b>Article Contents</b>:</span>
												<ul class="aspaBullets">
														<li>
																<a href="http://aspalliance.com/1156#Page1">Introduction </a>
														</li>
														<li>
																<a href="http://aspalliance.com/1156#Page2">Javascript File </a>
														</li>
														<li>
																<a href="http://aspalliance.com/1156#Page3">Code Behind </a>
														</li>
														<li>
																<a href="http://aspalliance.com/1156#Page4">Conclusion </a>
														</li>
												</ul>
										</div>
										<div id="RectangleAd"> </div>
								</td>
						</tr>
						<tr>
								<td align="middle" colspan="2">
										<hr class="line" />
								</td>
						</tr>
						<tr>
								<td class="subtitle" colspan="2">
										<span id="intellitTxt">
												<a class="pageTitle" id="#Page1">Introduction</a>
										</span>
								</td>
						</tr>
						<tr>
								<td class="content" colspan="2">
										<div class="backToTop">[ <a href="http://aspalliance.com/1156#top">Back To Top</a> ]</div>
										<br />
										<p class="MsoNormal" nd="1">After we have explored how to create <a href="http://aspalliance.com/1092_Rich_Text_Editor_Part_I">a rich text editor</a>, it is time to add some new features which will extend its usability and benefits. In this article we will get to know how new features were implemented. Again, all the major functionalities are written in javascript.</p>
										<p class="MsoNormal" nd="2">In the previous article you saw how easy it was to create your own text editor. Of course, as a first version the editor contained standard features. Now, after I had sometime to add new functionalities, I can say it contains more features and it is more robust. </p>
										<p class="MsoNormal">Below are the newly added features. </p>
										<p class="MsoListNumber" nd="3">1.<span style="FONT: 7pt 'Times New Roman'">    </span>Strike Through: This will strike through the text entered by the user.</p>
										<p class="MsoListNumber" nd="4">2.<span style="FONT: 7pt 'Times New Roman'">    </span>Decrease-Increase Indent: This will decrease or increase the text indent.</p>
										<p class="MsoListNumber" nd="5">3.<span style="FONT: 7pt 'Times New Roman'">    </span>Insert Images: This will open a new window giving the user the ability to upload an image to the <a class="iAs" style="COLOR: darkgreen; BORDER-BOTTOM: darkgreen 1px solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://aspalliance.com/1156#" target="_blank" itxtdid="3563345">server</a> and insert it directly into the editor.</p>
										<p class="MsoListNumber" nd="6">4.<span style="FONT: 7pt 'Times New Roman'">    </span>Copy, Cut, and Paste: This will copy, cut or paste the text entered by the user into the clipboard.</p>
										<p class="MsoListNumber" nd="7">5.<span style="FONT: 7pt 'Times New Roman'">    </span>Print: It allows only the text to be printed by opening the Print Setup dialog.</p>
										<p class="MsoListNumber" nd="8">6.<span style="FONT: 7pt 'Times New Roman'">    </span>Bulleted List &amp; Numbered List: It adds a bullet or number at the beginning of the text.</p>
										<p class="MsoListNumber" nd="9">7.<span style="FONT: 7pt 'Times New Roman'">    </span>Super-Sub script: It will format the text into super script or sub script.</p>
										<p class="MsoListNumber">8.<span style="FONT: 7pt 'Times New Roman'">    </span>Insert Lines: It adds lines to the text.</p>
										<p class="SectionHeading">Add HTML code to the user control</p>
										<p class="MsoNormal" nd="10">In this section we will add new HTML elements to add more features into the toolbar. We will place images for all the above mentioned actions and handle all the events to perform their functionalities.</p>
										<p class="CodeListingHeading">Listing 1</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> </span>
												</pre>
										</div>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">&lt;</span>
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: maroon; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">IMG</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">class</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=StrikeOut</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">id</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=Strikethrough</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">onmouseover</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">="ChangeImg('Strikethrough','strikethrough.over.gif')"</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">title</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">="Strike Through"</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">onclick</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">="Formats('StrikeThrough','&lt;%= this.HamEditorChildID %&gt;' )"</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">onmouseout</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">="ReturnImg('Strikethrough','strikethrough.gif',imgStatusUnderLine)"</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">src</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">="Images/strikethrough.gif"</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">&gt;</span>
 
 </span>
												</pre>
										</div>
										<p class="MsoNormal" nd="11">In the above listing we added an img HTML control. The onmouseover event will call a javascript function (which I already explained in the previous article), will change the image to a selected one, the onclick event will call another function which will apply the formatting into the selected text, and onmouseout will return the image to its previous state. For the rest of the features we will use the same concept except for the Insert Images.</p>
										<p class="MsoNormal" nd="12">Please download the project to see exactly how we created all the HTML tags.</p>
										<p class="CodeListingHeading">Figure 1 </p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> </span>
												</pre>
										</div>
										<p class="MsoNormal">
												<img height="302" src="http://aspalliance.com/ArticleFiles/1156/image001.jpg" width="594" border="0" />
										</p>
										<p class="MsoNormal"> </p>
								</td>
						</tr>
						<tr>
								<td class="subtitle" colspan="2">
										<span id="intellitTxt">
												<a class="pageTitle" id="#Page2">Javascript File</a>
										</span>
								</td>
						</tr>
						<tr>
								<td class="content" colspan="2">
										<div class="backToTop">[ <a href="http://aspalliance.com/1156#top">Back To Top</a> ]</div>
										<br />
										<p class="MsoNormal" nd="13">In this section we will add the new functions to the already created javascript file to handle the events for all the new features. They are all defined in one javascript file linked to this editor.</p>
										<p class="CodeListingHeading">Listing 2</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
function Formats(style,editorId)
{
// Save the Iframe id
var finalDivId = editorId + '_content';
                  
// Set the focus back to the text
document.frames[finalDivId].focus();
                  
// Apply the new style
document.frames[finalDivId].document.execCommand(style);
                  
// Set the focus back to the text
document.frames[finalDivId].focus();
}</span>
												</pre>
										</div>
										<p class="CodeListingHeading">
												<span style="TEXT-DECORATION: none">
												</span> </p>
										<p class="MsoNormal" nd="14">The above function is used by almost all the new event handlers. It takes two parameters; the first is the style to be applied, for example Copy, Paste, etc; the second is the editor id. We used the same function used before (execCommand) to apply the transformation.</p>
										<p class="CodeListingHeading">
												<span style="TEXT-DECORATION: none">
												</span> </p>
										<p class="CodeListingHeading">Listing 3</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">function SetBorders(id)
{
// set the borders to the emoticons icons onmouseover
var imgBorder = document.getElementById(id);
imgBorder.style.borderStyle = "solid";
imgBorder.style.borderWidth = "thin";
imgBorder.style.borderColor = "#688B9A";
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="15">The above function is being called when the insert emoticons div layer is shown. When the mouse is over an emotion, what we do is call this function which will set the borders for the table cell. In this way, it will be obvious for the user to see that he is over a specific emoticon. Notice the x image we added, it allows the user to close the div layer if he does not want to add any emoticons.</p>
										<p class="CodeListingHeading">Figure 2</p>
										<p class="MsoNormal">
												<img height="311" src="http://aspalliance.com/ArticleFiles/1156/image002.jpg" width="598" border="0" />
										</p>
										<p class="CodeListingHeading">Listing 4</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">function ClearBorders(id)
{
// Clear the borders of the emoticons icons onmouseout event
var imgBorder = document.getElementById(id);
imgBorder.style.borderStyle = "solid";
imgBorder.style.borderWidth = "thin";
imgBorder.style.borderColor = "white";
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="16">The above function will be called when the mouse is out of a specific emotion to clear its borders.</p>
										<p class="CodeListingHeading">Listing 5</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">function SetImage(editorId,path,e)
{
// Get the click location
var height = e.clientY + parseInt('5');
// Get the height inside the clicked image
var offsetHeight = parseInt(e.offsetY);
height = height - offsetHeight;
                  
// Get the click location(width)
var width = e.clientX
// Get the width inside the clicked image
var offsetWidth = parseInt(e.offsetX);
width = width - offsetWidth;
                  
// Save the iframe id
var finalDivId = editorId + '_content';
path = unescape(path);
                        
// Insert an image from the users Computers. 
window.open('UploadImages.aspx?path=' + path +
 '&amp;f=' + finalDivId,null,'width=500px,height=50px,titlebar=no,menubar=no,statusbar=no,toolbar=no,top='
 + height + 'left=' + width );             
                        
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="17">For the insert images new feature, we created a new webform called UploadImages.aspx. This form will be open when the user clicks on insert images icon to allow the user to select an image from his/her computer and upload it into the server in order to be able to insert it into the text editor. It contains two other fields to let the user specify the width and height of the image he/she wants to insert after it was uploaded. Below is a picture of the text editor when this new window is being opened.</p>
										<p class="CodeListingHeading">Figure 3</p>
										<p class="MsoNormal">
												<img height="307" src="http://aspalliance.com/ArticleFiles/1156/image003.jpg" width="607" border="0" />
										</p>
										<p class="MsoNormal" nd="18">The user has the ability to browse his/her computer to select an image and in its code behind and we are handling the upload part into the web server. I gave a great option for the administrator to set the directory on the web server where the image is going to be uploaded. I will explain it in detail in the code behind part. The maximum number of digits of the height and width properties is 3. The minimum value for the height property is 100 px and the maximum value of the width property is 150 px; as you can see they are their default values.</p>
										<p class="CodeListingHeading">Listing 6</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">function insertsImage(imageurl,editorId,height,width)
{
opener.document.frames[editorId].focus();
            
if(imageurl != "" | editorId!= "")
{
imageurl = unescape(imageurl);
var imageurl = imageurl + '"'  + "width=" + width + "px" + " " + "height="+ height + "px";
  // Save the iframe id
opener.document.frames[editorId].document.execCommand('InsertImage',false,imageurl);
}
opener.document.frames[editorId].focus();
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="19">This function will be called when the user clicks on insert image. In its click event we are uploading the file and then inserting the image through javascript into the text editor with the specified width and height.</p>
										<p class="CodeListingHeading">Figure 5</p>
										<p class="MsoNormal">
												<img height="318" src="http://aspalliance.com/ArticleFiles/1156/image004.jpg" width="624" border="0" />
										</p>
								</td>
						</tr>
						<tr>
								<td class="subtitle" colspan="2">
										<span id="intellitTxt">
												<a class="pageTitle" id="#Page3">Code Behind </a>
										</span>
								</td>
						</tr>
						<tr>
								<td class="content" colspan="2">
										<div class="backToTop">[ <a href="http://aspalliance.com/1156#top">Back To Top</a> ]</div>
										<br />
										<p class="MsoNormal" nd="20">In this section we will see how the upload images code behind is implemented and how the administrator of this editor can set the default directory where the images can be uploaded.</p>
										<p class="CodeListingHeading">
												<span style="TEXT-DECORATION: none">
												</span> </p>
										<p class="CodeListingHeading">Listing 7</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> FilePath
{
get
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">return</span> _filePath;
}
set
{
_filePath <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> Server.HtmlEncode(Request.ApplicationPath <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> value);
}
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="21">The above listing is a property of type string which the administrator can use to set the default upload directory. The below listing will show you how to use it in your webform to specify the directory. This path will be sent to the UploadImages.aspx page query string when it is being opened from the javascript function.</p>
										<p class="MsoNormal"> </p>
										<p class="CodeListingHeading">Listing 8</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> imagePath <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"/UserImages"</span>;
((hamHtmlEditor)<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">this</span>.FindControl(<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"HamHtmlEditor1"</span>)).FilePath <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> imagePath;</span>
												</pre>
										</div>
										<p class="CodeListingHeading">Listing 9</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> Location
{
get
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">return</span> _location;
}
set
{
_location <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> value;
}
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="22">The above property is being used to get the editor ID in order to be able to insert it directly after being uploaded to the web server.</p>
										<p class="CodeListingHeading">Listing 10</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">if</span>(heightValidator.IsValid &amp;&amp; widthValidator.IsValid)
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> elementToInsert <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> Request.QueryString[<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"f"</span>];
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">if</span>(UploadImage.PostedFile !<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">null</span> &amp;&amp; UploadImage.PostedFile.ContentLength &gt; 0)
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">try</span>
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> fileName <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> System.IO.Path.GetFileName(UploadImage.PostedFile.FileName);
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> fileLocation <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> Request.QueryString[<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"path"</span>] <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"/"</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> fileName; 
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">this</span>.ImagePath <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> Server.HtmlEncode(fileLocation);
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">this</span>.Location <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> elementToInsert;
Button1.Enabled <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">false</span>;
UploadImage.PostedFile.SaveAs(Server.MapPath(fileLocation));
}
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">catch</span>(Exception ex)
{
Response.Write(<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"&lt;script language=javascript&gt;alert('"</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span>
ex.Message.ToString().Replace(<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">@"\"</span>,<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">@"\\"</span>)<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span><span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"');&lt;/script&gt;"</span>);
}
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">finally</span>
{
Button1.Enabled <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">true</span>;
}
}
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="23">The above code is written inside the insert images' button click. Its purpose is to upload the image to the already specified directory after it checks if the height and width values are correct. The try and catch blocks are used to handle exceptions.</p>
										<p class="MsoNormal" nd="24">
												<span class="Bold">NOTE</span>: One important thing Administrator has to note is he/she has to give writen permission to ASPNET user to access UserImages folder or to the folder path which is in the imagePath variable. This change is needed to write/upload an image to the desired folder.</p>
										<p class="CodeListingHeading">Listing11</p>
										<div class="MsoNormal">
												<pre>
														<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
																<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span> <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">bool</span> ShowHeader
{
get
{
<span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">return</span> _Header;
}
set
{
_Header <span style="FONT-WEIGHT: normal; FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> value;
}
}</span>
												</pre>
										</div>
										<p class="MsoNormal" nd="25">A new property of type Boolean is introduced. Once it is set to false the toolbar will not show up. I added this property to give you the ability to show only read-only text after the text is being posted.</p>
										<p class="CodeListingHeading">Figure 6</p>
										<p class="MsoNormal">
												<img height="236" src="http://aspalliance.com/ArticleFiles/1156/image005.jpg" width="599" border="0" />
										</p>
										<p class="MsoNormal" nd="26">The above figure is the rich text editor when its ShowHeader property is set to false. The toolbar and the below other icons will be invisible. In this way, you can use it only for displaying your saved text.</p>
										<p class="CodeListingHeading">Figure 7</p>
										<p class="MsoNormal">
												<img height="315" src="http://aspalliance.com/ArticleFiles/1156/image006.jpg" width="624" border="0" />
										</p>
										<p class="MsoNormal" nd="27">Here is a final version of the editor with all the new features being used. </p>
								</td>
						</tr>
						<tr>
								<td class="subtitle" colspan="2">
										<span id="intellitTxt">
												<a class="pageTitle" id="#Page4">Conclusion</a>
										</span>
								</td>
						</tr>
						<tr>
								<td class="content" colspan="2">
										<div class="backToTop">[ <a href="http://aspalliance.com/1156#top">Back To Top</a> ]</div>
										<br />
										<p class="MsoNormal" nd="28">This editor is being tested on IE only for this release. The next release I will work on making it workable on multiple browsers. Once again, your ideas are welcome to enhance this editor or to report any bug. I will try to make it as a custom control which will be easier for you to use in the future. Hope you gained useful information throughout this article.</p>
										<p class="MsoNormal">
												<a href="http://authors.aspalliance.com/HabdulMalak/1156.zip">Source code of the Article</a>
												<br />
												<a href="/Files/MartinYao/1156.zip">Click here download it.</a>
												<br />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24400.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-21 23:42 <a href="http://www.cnitblog.com/MartinYao/articles/24400.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Stratifier Layering ASP.NET Application Code</title><link>http://www.cnitblog.com/MartinYao/articles/24399.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 21 Mar 2007 15:33:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24399.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24399.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24399.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24399.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24399.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: It seems like more and more is being asked of developers these days, and more and more we are expected to be coding superheroes. Although development platforms are becoming increasingly productive, mo...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/24399.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/24399.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-21 23:33 <a href="http://www.cnitblog.com/MartinYao/articles/24399.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>窗體關閉相關</title><link>http://www.cnitblog.com/MartinYao/articles/24375.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 21 Mar 2007 05:29:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24375.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24375.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24375.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24375.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24375.html</trackback:ping><description><![CDATA[
		<p>關閉窗體關閉事件</p>
		<p>方式一：<br />  &lt;script   language="javascript"&gt;       <br />  function     window.onbeforeunload()       <br />  {       <br />  if(event.clientX&gt;document.body.clientWidth&amp;&amp;event.clientY&lt;0   ||event.altKey)       <br />  {       <br />  var     xmlhttp     =     new     ActiveXObject("Microsoft.XMLHTTP");   <br />    <br />  xmlhttp.open("GET","exit.aspx",false);       <br />  xmlhttp.send();   <br />  }   <br />  }       <br />  &lt;/script&gt;   <br />  然后在exit.aspx里   <br />  Session.Clear();</p>
		<p>&lt;iframe  name=body  frameBorder=0    <br />改为  <br />&lt;iframe  name=body1  frameBorder=0</p>
		<p>
				<br />方式二：<br />在页面body   onbeforeunload   事件里提交服务器，设置session立即过期。   <br />  ...   <br />  &lt;script   language=javascript&gt;   <br />  function   Quit()   <br />  {   <br />          document.all.clientCommand   =   "Quit";   <br />          document.forms[0].submit();   <br />  }   <br />  &lt;/script&gt;   <br />  &lt;body   onbeforeunload="Quit()"&gt;   <br />  ...   <br />  &lt;input   type="hidden"   id="clientCommand"   runat="server"&gt;   <br />  ...   <br />  &lt;/body&gt;   <br />    <br />  codebehide   <br />  ===========   <br />  Page_Load事件处理方法中：   <br />    <br />  if(this.clientCommand.Value   ==   "Quit")   <br />        this.Session.TimeOut   =   -1;   </p>
		<p>
				<br />枚舉窗體然後關閉<br />  1）得到窗体的数量   <br />  2）得到打开窗体的句柄       <br />  1）好解决，因为你的窗体都是有一个父窗口打开的，所以在这个父窗口中使用cookie记录下带开窗口的最大数就行了。这样就能再另一个地方通过cookie和循环得到打开窗口的名称。   <br />  2）也不难，因为我们知道打开窗口的名称了。我们使用window.open()函数得到打开窗口的句柄。   <br />  所以这样解决：   <br />  var   wmax=cookie中窗口的最大数量;   <br />  var   wopened;   <br />  for(i=0;i&lt;wmax;i++){   <br />         wopened=window.open("","abcwindow_"   +   i,"top=2000,width=1500");   <br />      wopened.close();   <br />  }   <br />  完成。   </p>
		<p>關閉窗體清除Cache<br />&lt;body   onbeforeunload="window.open('clear.aspx','','top=4000')"&gt;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-21 13:29 <a href="http://www.cnitblog.com/MartinYao/articles/24375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>防止同一用户同时登陆</title><link>http://www.cnitblog.com/MartinYao/articles/24362.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 20 Mar 2007 12:11:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24362.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24362.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24362.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24362.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24362.html</trackback:ping><description><![CDATA[
		<p>要防止同一用户同时登陆,首页应该记录在线用户的信息(这里与用户名为例),然后判断正在登陆的用户里面是否已存在．在这里使用一个cache存放已经登陆的用户名．但是还有一个问题就是要知道用户是什么时候离开系统的呢？这就要定期清除cache中的内容了，也就是设置一个cache的时间．这个时间可以跟用户的session值联系起来．刚好当用户session值失效的时候该用户在cache里面的信息也会被清空．这样就达到了防止同时登陆的效果，具体代码如下： </p>
		<p>放在登陆成功的地方 </p>
		<p>string key = TextBox1.Text; //用户名文本框设为cache关键字 <br />string uer = Convert.ToString(Cache[key]); //读取cache中用户相应的值 <br />//判断cache中是否有用户的信息，如果没有相关的值，说明用户未登陆 <br />if (uer == null 　　 uer == String.Empty) <br />{ </p>
		<p>//定义cache过期时间 <br />TimeSpan SessTimeout = new TimeSpan(0, 0, System.Web.HttpContext.Current.Session.Timeout, 0, 0); </p>
		<p>//第一次登陆的时候插入一个用户相关的cache值， <br />HttpContext.Current.Cache.Insert(key, key, null, DateTime.MaxValue, SessTimeout, System.Web.Caching.CacheItemPriority.NotRemovable, null); <br />Session["ADMINID"] = TextBox1.Text; <br />Response.Redirect("main.aspx"); <br />} <br />else <br />{ <br />//重复登陆 <br />Response.Write(""); <br />}</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24362.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-20 20:11 <a href="http://www.cnitblog.com/MartinYao/articles/24362.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在aspx页面中添加客户端事件 </title><link>http://www.cnitblog.com/MartinYao/articles/24354.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 20 Mar 2007 07:45:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24354.html</guid><description><![CDATA[
		<div class="postTitle"> </div>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #0000ff">public</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> popmessage(Page p,String message) <br /><img id="Codehighlighter1_47_125_Open_Image" onclick="this.style.display='none'; Codehighlighter1_47_125_Open_Text.style.display='none'; Codehighlighter1_47_125_Closed_Image.style.display='inline'; Codehighlighter1_47_125_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_47_125_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_47_125_Closed_Text.style.display='none'; Codehighlighter1_47_125_Open_Image.style.display='inline'; Codehighlighter1_47_125_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_47_125_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.cnblogs.com/Images/dot.gif" />
				</span>
				<span id="Codehighlighter1_47_125_Open_Text">
						<span style="COLOR: #000000">{ <br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />p.Response.Write(</span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000">&lt;body onload = javascript:alert('</span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000"> message </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000">');&gt;</span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000">); <br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000"> <br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />使用函数:popmessage(</span>
				<span style="COLOR: #0000ff">this</span>
				<span style="COLOR: #000000">,</span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000">对不起，当前操作执行失败'</span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000">);<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span>
		</div>
		<br />关于如何在服务器端控件上添加客户端事件。首先创建一个页面 
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">&lt;%</span><span style="COLOR: #000000">@ Page Language</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">C#</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> AutoEventWireup</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">true</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">  CodeFile</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Default.aspx.cs</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> Inherits</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_Default</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">%&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;!</span><span style="COLOR: #000000">DOCTYPE html PUBLIC </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">-//W3C//DTD XHTML 1.0 Transitional//EN</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">html xmlns</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">http://www.w3.org/1999/xhtml</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">head runat</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">title</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">Untitled Page</span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">title</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />   <br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">head</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">body</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">form id</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">form1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> runat</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">div</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">asp:Button ID</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> runat</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> OnClick</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button1_Click</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> Text</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">客户端事件演示-1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">br </span><span style="COLOR: #000000">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">asp:Button ID</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button2</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> runat</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> OnClick</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button2_Click</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> Text</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">客户端事件演示-2</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">/&gt;&lt;</span><span style="COLOR: #000000">br </span><span style="COLOR: #000000">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">asp:Button ID</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button3</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> runat</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> Text</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">添加控件客户端事件</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> OnClick</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Button3_Click</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">/&gt;&lt;</span><span style="COLOR: #000000">br </span><span style="COLOR: #000000">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">div</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">form</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">body</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;/</span><span style="COLOR: #000000">html</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span></div><br />相应的cs代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> partial </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> _Default : System.Web.UI.Page <br /><img id="Codehighlighter1_52_674_Open_Image" onclick="this.style.display='none'; Codehighlighter1_52_674_Open_Text.style.display='none'; Codehighlighter1_52_674_Closed_Image.style.display='inline'; Codehighlighter1_52_674_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_52_674_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_52_674_Closed_Text.style.display='none'; Codehighlighter1_52_674_Open_Image.style.display='inline'; Codehighlighter1_52_674_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_52_674_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_52_674_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br /><img id="Codehighlighter1_115_251_Open_Image" onclick="this.style.display='none'; Codehighlighter1_115_251_Open_Text.style.display='none'; Codehighlighter1_115_251_Closed_Image.style.display='inline'; Codehighlighter1_115_251_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_115_251_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_115_251_Closed_Text.style.display='none'; Codehighlighter1_115_251_Open_Image.style.display='inline'; Codehighlighter1_115_251_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span id="Codehighlighter1_115_251_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_115_251_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">!</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.IsPostBack)<br /><img id="Codehighlighter1_155_245_Open_Image" onclick="this.style.display='none'; Codehighlighter1_155_245_Open_Text.style.display='none'; Codehighlighter1_155_245_Closed_Image.style.display='inline'; Codehighlighter1_155_245_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_155_245_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_155_245_Closed_Text.style.display='none'; Codehighlighter1_155_245_Open_Image.style.display='inline'; Codehighlighter1_155_245_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span><span id="Codehighlighter1_155_245_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_155_245_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.Button3.Attributes.Add(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onclick</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">return confirm('确定吗？');</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Button1_Click(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br /><img id="Codehighlighter1_318_460_Open_Image" onclick="this.style.display='none'; Codehighlighter1_318_460_Open_Text.style.display='none'; Codehighlighter1_318_460_Closed_Image.style.display='inline'; Codehighlighter1_318_460_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_318_460_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_318_460_Closed_Text.style.display='none'; Codehighlighter1_318_460_Open_Image.style.display='inline'; Codehighlighter1_318_460_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span id="Codehighlighter1_318_460_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_318_460_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.RegisterClientScriptBlock(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ClientScript</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;script language=javascript&gt;alert('客户端事件演示-注册客户端事件！添加脚本在&lt;form&gt;后面');&lt;/script&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Button2_Click(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br /><img id="Codehighlighter1_527_666_Open_Image" onclick="this.style.display='none'; Codehighlighter1_527_666_Open_Text.style.display='none'; Codehighlighter1_527_666_Closed_Image.style.display='inline'; Codehighlighter1_527_666_Closed_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_527_666_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_527_666_Closed_Text.style.display='none'; Codehighlighter1_527_666_Open_Image.style.display='inline'; Codehighlighter1_527_666_Open_Text.style.display='inline';" src="http://liughost.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span><span id="Codehighlighter1_527_666_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_527_666_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.RegisterStartupScript(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ClientScript</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;script language=javascript&gt;alert('客户端事件演示-注册客户端事件！添加脚本在&lt;/form&gt;前面');&lt;/script&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img src="http://liughost.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div>这个例子中button1按钮添加一段javascript脚本在&lt;form&gt;后面，button2添加javascript脚本在&lt;/form&gt;前面。<br />邵志东老师的asp.net事件的讲座下载地址<a title="asp.net事件的讲座" href="http://download.microsoft.com/download/D/7/0/D7095FB7-8F5A-4DF8-B8CA-CA3F7BE7306D/WebCast20060224am_Video.zip">asp.net事件的讲座</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/24354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-20 15:45 <a href="http://www.cnitblog.com/MartinYao/articles/24354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Push and Run .NET Code on Remote Machine</title><link>http://www.cnitblog.com/MartinYao/articles/23922.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 12 Mar 2007 12:35:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/23922.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/23922.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/23922.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/23922.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/23922.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely_src.zip">Download source files - 69.7 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely_demo.zip">Download demo project - 43.7 Kb</a>
				</li>
		</ul>
		<p>
				<img height="242" alt="Sample Image - Run_Code_Remotely.png" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Pushing_Code_Drawing.png" width="592" align="middle" />
		</p>
		<p>
				<img height="236" alt="Sample Image - Run_Code_Remotely.png" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely.png" width="600" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">I've been doing Windows programming for a long time, and I've always longed for the ability to do what some of the Unix guys have always been able to do: run commands on a remote machine. Although, there's a small twist. I really don't want to run some shell commands on a remote machine, but rather write some code, push it over to a remote machine, and have it execute over there without manually copying the files over. Not only that, but I'd like to interact with the process locally with standard in, standard out and standard error. What this will allow me to do is to write some administrative code and run the assembly on the remote machine(s) without any special hooks into my admin code. As well, if I have some process that will take a bunch of time to run, I can simply have it run on another machine, and report back the status and results without consuming time on my own machine. The latter approach leads to a more general subject of viewing any machine as a general computing resource.</p>
		<h2>Quick Example (output from the image above)</h2>
		<p nd="2">The image above is the output of an assembly that simply functions as a .NET version of "DIR". Basically, a search was issued for <i>*.mp3</i> on the local machine and the results were displayed. Next, the search was issued <b>in parallel</b> to all the machines that matched the name "testwin*". In this case, two machines matched that name. I ran the second one just to show that it could be run explicitly as well.</p>
		<h3>Give it a try!</h3>
		<p nd="3">If you'd like to test it out, download the demo project from the link above into a folder. Here's the usage:</p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="4" preid="0"> Collapse</span>
		</div>
		<pre lang="text" id="pre0" style="MARGIN-TOP: 0px" nd="5">Copyright (C) 2004-2005 Jim Wiese 
 
Executes the specified assembly on the remote machine and the remote <br />application will function interactively. 
 
 Usage: \\machine[,\\machine2,[...]] [-port portNumber] [-serial]<br />   [-interactive] [-timeout [days.]HH:MM:SS] [-remoteUserId userId] <br />   [-remotePassword password] [-unmanaged pathToZipFile.zip]<br />   [-debug] assembly [arguments] 
 
machine: The machine to run the assembly on. Note that this value can have <br />    the '*' character to represent multiple machines. As well, \* will match<br />    all the machines in the domain. (eg HP* -&gt; HP1, HP2) 
port:    The port number to use for the communication between processes 
    assembly: The assembly name to run on the remove machine. This assembly<br />    must have an entry point defined (ie Main). 
arguments: Any arguments to pass to the executed assembly. 
debug: Write debugging information to the standard out. 
serial:     Run the machines serially, one at a time. By default, all the <br />    machines are run in parallel. 
interactive: Allow the process to interact with the desktop process 
timeout:     Allow the process to run for the specified amount of time <br />    remoteUserId: The user ID to run under on the remote process <br />    remotePassword: The password for the remote user ID 
unmanaged:     The path to a zip file with the unmanaged resources to <br />    decompress on the remote machine


</pre>
		<h2>Possible uses for this library / tool</h2>
		<ul>
				<li nd="6">Run-time deployment of Application servers (eg for business logic) to a specific set of machines 
</li>
				<li nd="7">Run-time deployment of web servers to a specific set of machinesPerform administrative functions on Windows Domain clients such as run time installation of software 
</li>
				<li nd="8">Determine if specific files are stored on Windows machines <b>without the need for UNC shares</b> (eg. C:\&gt; runremote \\* quickdir.exe *.mp3) 
</li>
				<li nd="9">Create an on demand "Peer Grid" to solve problems such as sorting a large file before import into a database 
</li>
				<li nd="10">Force GPO updates on the client machines immeadiately 
</li>
				<li nd="11">Determine if someone is actually logged onto a console of a machine 
</li>
				<li nd="12">Log people off the console of the machine if they're logged in :) (eg for Kiosk purposes)</li>
		</ul>
		<h2>Sweetness, how does it work?</h2>
		<p nd="13">The main premise of the process is quite simple, but with a couple of small "gotchas". First, the process creates a temporary Windows service on the remote machine with a random name. I say random in the sense that a GUID is appended to the service name. This service creates a child process that will execute the specified assembly. The service waits for the child process to finish executing the assembly, then removes itself (i.e., the Service entry) from the remote machine, and exits.</p>
		<p nd="14">The interesting premise comes in two different steps:</p>
		<ol>
				<li nd="15">How does the service get the executable to run? 
</li>
				<li nd="16">How are the assemblies shuffled over to the remote machine? </li>
		</ol>
		<h2>Pushing the executable to run the temp service</h2>
		<p nd="17">Here's the first of the two "gotchas" from above: the service code is copied over to the UNC folder <i><a href="file://remotemachine/admin$/temp">file://remoteMachine/admin$/temp</a></i>. Windows exposes the administrative share named <i>Admin$</i> by default, and we make use of this share to get the executable over to the remote machine. We could have created a service that used <i>\\sourceMachine\Admin$\temp</i> (i.e., the machine from which the command was run), but any .NET code that is run under a UNC uses a much more restrict security policy. These temporary files will be removed when the entire process is finished. As a matter of fact, it goes to great length to try to make sure that the files are removed once all is finished.</p>
		<p nd="18">The one main thing to keep in mind is that the sole purpose of the service is to create a remote process. Some people refer to this as a hack method of starting processes, but in this case, I consider it a feature.</p>
		<h2>But what about the assembly that I want you to run?</h2>
		<p nd="19">The next important thing to consider is that we have not actually copied the assembly that is going to be run over to the remote machine. This is where a small piece of elegance comes in the code. What the service shim does is attempt to load "YourAssembly". Since this assembly does not exist on the remote machine, a Loader exception will occur. There is a handy little event that is fired when this happens in an AppDomain named:</p>
		<pre lang="cs" nd="29">
				<span class="cs-comment" nd="20">/// &lt;summary&gt;</span>
				<span class="cs-comment" nd="21">/// Event that is called when the domain can not find an assembly.</span>
				<span class="cs-comment" nd="22">/// We want to lookup this assembly on the remote machine and return it.</span>
				<span class="cs-comment" nd="23">/// &lt;/summary&gt;</span>
				<span class="cs-comment" nd="24">/// &lt;param name="sender"&gt;The current AppDomain&lt;/param&gt;</span>
				<span class="cs-comment" nd="25">/// &lt;param name="args"&gt;Event arguments for this event&lt;/param&gt;</span>
				<span class="cs-comment" nd="26">/// &lt;returns&gt;The assembly from the remote machine</span>
				<span class="cs-comment" nd="27">///          or null if it wasn't found&lt;/returns&gt;</span>
				<span class="cs-keyword" nd="28">private</span> Assembly CurrentDomain_AssemblyResolve(<span class="cs-keyword" nd="30">object</span> sender,
                                       ResolveEventArgs args)
{
  <span class="cs-keyword" nd="31">return</span> ResolveAssemblyOnRemoteMachine( args.Name ) ;
}</pre>
		<p nd="32">This event gives the assembly name that was attempted to be loaded in the arguments, and allows the block of code a chance to locate it and return it. In this case, a small block of code is put in to connect back to the source machine and request the lookup on that machine.</p>
		<p nd="33">Since the code does exist on the source machine, the bytes of the assembly are packaged up and sent back to the remote machine. The assembly is then loaded from these bytes and returned to the AppDomain loader.</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="34" preid="2"> Collapse</span>
		</div>
		<pre lang="cs" id="pre2" style="MARGIN-TOP: 0px" nd="40">
				<span class="cs-comment" nd="35">/// &lt;summary&gt;</span>
				<span class="cs-comment" nd="36">/// Resolve the assembly from the remote machine (machine from which the <br />/// command was run). If the assembly exists on the remote machine, it <br />//  will be shuffled over to this process and then loaded and returned.</span>
				<span class="cs-comment" nd="37">/// &lt;/summary&gt;</span>
				<span class="cs-comment" nd="38">/// &lt;param name="assemblyName"&gt;The name of the assembly to resolve&lt;/param&gt;</span>
				<span class="cs-comment" nd="39">/// &lt;returns&gt;The assembly if it existed, otherwise null&lt;/returns&gt;</span>
Assembly ResolveAssemblyOnSourceMachine( <span class="cs-keyword" nd="41">string</span> assemblyName )
{
   <span class="cs-comment" nd="42">//</span><span class="cs-comment" nd="43">// This is the main point in which we shuffle</span><span class="cs-comment" nd="44">// the assembly from the server to this</span><span class="cs-comment" nd="45">// client. //</span><span class="cs-keyword" nd="46">byte</span>[] assemblyBytes = server.GetAssembly( assemblyName ) ;
   <span class="cs-keyword" nd="47">if</span> ( assemblyBytes != <span class="cs-keyword" nd="48">null</span> &amp;&amp; assemblyBytes.Length &gt; <span class="cs-literal" nd="49">0</span> )
   {
     Assembly loaded = Assembly.Load( assemblyBytes ) ;
     <span class="cs-keyword" nd="50">return</span> loaded ;
   }
   <span class="cs-keyword" nd="51">else</span>
   {
     <span class="cs-keyword" nd="52">return</span><span class="cs-keyword" nd="53">null</span> ;
   }
}</pre>
		<p nd="54">At this point, you'll notice that I referenced a small variable named "<code nd="55">server</code>" which seems to get the bytes of the assembly. This "server" variable is actually a .NET Remoted object that is being served up by our source machine. There are probably a variety of ways that this could be done, but the .NET remoting method was too clean and easy to pass up.</p>
		<p nd="56">Once the assembly that you requested to be run is loaded, the EntryPoint is invoked with any command line arguments that you might want to pass in. Typically, the EntryPoint is the "<code lang="cs" nd="57">Main</code>" of your assembly. After your <code lang="cs" nd="58">Main( <span class="cs-keyword" nd="59">string</span>[] args )</code> is run, the return code is reported back to the source machine and the process on the remote machine exists. To appear like a local process, the "<i>runremote.exe</i>" process on the source machine exists with the same return code of the remote process so that you can use shell utilities to check the return code.</p>
		<p nd="60">Now, your executing assembly might have also needed some other dependencies. If it references those dependencies, won't it cause a problem? Thankfully, the answer is no, it won't cause a problem. Any missing dependencies cause the <code nd="61">ResolveAssembly</code> event to be called and loaded by the same mechanism.</p>
		<h2>"Down by the water, out by the sea..." (??? grunge, circa 1993)</h2>
		<p nd="62">Now, if you've made this far in the article, I'm impressed and honored. Let's not delay and delve into the depths of the nitty and the gritty of the code:</p>
		<p nd="63">All the code starts in the project RunRemote in the file <i>RunRemote.cs</i> in the class <code nd="64">RemoteProcess</code>. This is the main project if you'd like to run it programmatically, but if you're running the command version, then it's the class <code nd="65">CommandLineRemoteProcess</code> which actually calls <code nd="66">RemoteProcess.ExecuteRemotely</code>. Basically, it's all fairly simple to call from the outside. Here's the sample code to actually run an assembly remotely:</p>
		<pre lang="cs" nd="67">RemoteProcess  remote     = <span class="cs-keyword" nd="68">new</span> RemoteProcess() ;
<span class="cs-keyword" nd="69">int</span>            exitCodeOfRemoteProcess   =
                    remote.ExecuteRemotely(
                        <span class="cpp-string" nd="70">"YourAssembly"</span>, <span class="cs-comment" nd="71">// Note: partial assembly name  </span><span class="cs-keyword" nd="72">new</span><span class="cs-keyword" nd="73">string</span>[] { <span class="cpp-string" nd="74">"-arg1"</span>, <span class="cpp-string" nd="75">"-arg2"</span> },
                        <span class="cs-keyword" nd="76">new</span><span class="cs-keyword" nd="77">string</span>[] { @<span class="cpp-string" nd="78">"\\onMyMachine"</span> },
                        <span class="cs-literal" nd="79">0</span>,
                        <span class="cs-literal" nd="80">0</span>,
                        <span class="cs-keyword" nd="81">false</span>,
                        <span class="cs-keyword" nd="82">new</span> TimeSpan( Timeout.Infinite ),
                        <span class="cs-literal" nd="83">1</span> ) ;

<span class="cs-comment" nd="84">//</span><span class="cs-comment" nd="85">// NOTE: There are some other overloaded versions of this method with </span><span class="cs-comment" nd="86">// more parameters, see the source code for details</span><span class="cs-comment" nd="87">//</span></pre>
		<p nd="88">Now, I've described before that a file is copied and a service is created. I'm not going to outline the code to copy files and create the service, but I'll give some specifics that are interesting. First, the output of the project "ServiceOnRemoteMachine" generates "<i>ServiceOnRemoteMachine.exe</i>". Take a wild guess at what this file is used for :). After this file is copied to the remote machine, the service is created with the file name of "<i>%SYSTEMROOT%\temp\ServiceOnRemoteMachine.exe</i>". This path name is the resolved value of the mapped path <i>\\remoteMachine\admin$\temp\ServiceOnRemoteMachine.exe</i>, but with a local reference (e.g., <i>C:\Windows\temp\ServiceOnRemoteMachine.exe</i>). As well, the service name of the service that is running is passed as an argument as well as the remoting URI to connect back to the source machine. The last argument is the name of the assembly to load (i.e., the name of your assembly). For example, the path for the service might be:</p>
		<pre lang="text" nd="89">"C:\WINDOWS\temp\serviceonremotemachine.exe" -fromService
         ExecRemote-e2088b28-330f-4ac6-9627-799ddd435004
         "tcp://dublin:3237/83c1ca40_8305_4583_bf5b_9265a03d9de4/RunRemote.rem"
         "runthisonothermachine"</pre>
		<p nd="90">When the service first starts up in the <code lang="cs" nd="91">Main()</code> method, the argument "-fromService" is noted. This tells the service to spawn a new process using itself as the executable, but removing the service name and the "-fromService" argument.</p>
		<h3>Process and AppDomain isloation for safety (Injecting the safe way)</h3>
		<img height="228" alt="" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Service_Process_Drawing.png" width="630" />
		<p>
		</p>
		<p nd="92">Now, in the new child process, a remoting object named "<code nd="93">Server</code>" is hooked up by the Activator and uses the URI to get back to the source machine.</p>
		<pre lang="cs" nd="97">
				<span class="cs-comment" nd="94">//</span>
				<span class="cs-comment" nd="95">// Setup the formatters</span>
				<span class="cs-comment" nd="96">//</span>
TcpChannel    channel        = <span class="cs-keyword" nd="98">new</span> TcpChannel() ;
ChannelServices.RegisterChannel( channel );

<span class="cs-comment" nd="99">// Create an instance on the remote server and call a method remotely</span>
server = (Server)Activator.GetObject(   <span class="cs-keyword" nd="100">typeof</span> ( Server ), <span class="cs-comment" nd="101">// Type to create</span>
                                        serverUri  );

</pre>
		<p nd="102">The whole rest of the code and purpose for the entire class comes to the final method. This method loads up the assembly, hooks up <code nd="103">STDIN</code>, <code nd="104">STDOUT</code>, <code nd="105">STDERR</code>, and runs the sucker.</p>
		<div class="precollapse" id="premain6" style="WIDTH: 100%">
				<img id="preimg6" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="6" />
				<span id="precollapse6" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="106" preid="6"> Collapse</span>
		</div>
		<pre lang="cs" id="pre6" style="MARGIN-TOP: 0px" nd="120">
				<span class="cs-comment" nd="107">/// &lt;SUMMARY&gt;</span>
				<span class="cs-comment" nd="108">/// Believe it or not, the entire application reduces to this one method. By</span>
				<span class="cs-comment" nd="109">/// this point, we are running in a process on the remote machine</span>
				<span class="cs-comment" nd="110">/// &lt;/SUMMARY&gt;</span>
				<span class="cs-comment" nd="111">/// <param name="assemblyName" />The name of the assembly to execute. This name</span>
				<span class="cs-comment" nd="112">/// can be either a partial name such as "RunThisOnOtherMachine" or a fully</span>
				<span class="cs-comment" nd="113">/// qualified name.</span>
				<span class="cs-comment" nd="114">/// </span>
				<span class="cs-comment" nd="115">/// <param name="args" />Arguments to send to the main method, or null for no args</span>
				<span class="cs-comment" nd="116">/// &lt;RETURNS&gt;The return result from the entry</span>
				<span class="cs-comment" nd="117">///   point of the requested assembly&lt;/RETURNS&gt;</span>
				<span class="cs-keyword" nd="118">public</span>
				<span class="cs-keyword" nd="119">object</span> LoadAssemblyAndRunIt( <span class="cs-keyword" nd="121">string</span> assemblyName, <span class="cs-keyword" nd="122">string</span>[] args )
{

    <span class="cs-comment" nd="123">//</span><span class="cs-comment" nd="124">// Setup the readers/writers to use the server's streams.  This will allow</span><span class="cs-comment" nd="125">// this process to read and write to the console of the process on the</span><span class="cs-comment" nd="126">// machine from which the command was run.</span><span class="cs-comment" nd="127">//</span>

    TextWriter    stdErr    = AssemblyResolutionServer.StdErr ;
    TextWriter    stdOut    = AssemblyResolutionServer.StdOut ;
    TextReader    stdIn    = AssemblyResolutionServer.StdIn ;

    m_sponsorManager.Register( stdErr );
    m_sponsorManager.Register( stdOut ) ;
    m_sponsorManager.Register( stdIn ) ;

    Console.SetError( <span class="cs-keyword" nd="128">new</span> NoExceptionTextWriter( stdErr ) ) ;
    Console.SetOut( <span class="cs-keyword" nd="129">new</span> NoExceptionTextWriter( stdOut ) ) ;
    Console.SetIn( stdIn ) ;

    <span class="cs-comment" nd="130">//</span><span class="cs-comment" nd="131">// Setup the event handlers for the domain</span><span class="cs-comment" nd="132">//</span>
    AppDomain.CurrentDomain.ResourceResolve 
                += <span class="cs-keyword" nd="133">new</span> ResolveEventHandler( CurrentDomain_ResourceResolve );
    AppDomain.CurrentDomain.AssemblyResolve    
                += <span class="cs-keyword" nd="134">new</span> ResolveEventHandler( CurrentDomain_AssemblyResolve );
    AppDomain.CurrentDomain.DomainUnload    
                += <span class="cs-keyword" nd="135">new</span> EventHandler(CurrentDomain_DomainUnload);

    <span class="cs-comment" nd="136">//</span><span class="cs-comment" nd="137">// Get any required unmanaged dependencies</span><span class="cs-comment" nd="138">//</span>
    RetrieveUnmanagedDependencies() ;

    Assembly    assemblyToExecute = AppDomain.CurrentDomain.Load( 
                                                           assemblyName ) ;

    <span class="cs-keyword" nd="139">object</span>        result          = <span class="cs-keyword" nd="140">null</span> ;

    <span class="cs-comment" nd="141">//</span><span class="cs-comment" nd="142">// Finally, execute the assembly</span><span class="cs-comment" nd="143">//</span><span class="cs-keyword" nd="144">try</span>
    {
        <span class="cs-keyword" nd="145">if</span> ( assemblyToExecute != <span class="cs-keyword" nd="146">null</span> )
        {
            <span class="cs-keyword" nd="147">if</span> ( assemblyToExecute.EntryPoint.GetParameters().Length == <span class="cs-literal" nd="148">0</span> )
            {
                result    =
                    assemblyToExecute.EntryPoint.Invoke( <span class="cs-keyword" nd="149">null</span>, <span class="cs-keyword" nd="150">null</span> ) ;
            }
            <span class="cs-keyword" nd="151">else</span>
            {
                result    =
                    assemblyToExecute.EntryPoint.Invoke( <span class="cs-keyword" nd="152">null</span>,
                                               <span class="cs-keyword" nd="153">new</span><span class="cs-keyword" nd="154">object</span>[]{ args } ) ;
            }

            
        }
    }
    <span class="cs-keyword" nd="155">catch</span>( Exception exp )
    {
        Trace.WriteLine( exp.Message + <span class="cpp-string" nd="156">"\n"</span> + exp.StackTrace, <span class="cpp-string" nd="157">"Error"</span> ) ;

        <span class="cs-keyword" nd="158">if</span> ( exp.InnerException != <span class="cs-keyword" nd="159">null</span> )
        {
            Trace.WriteLine( exp.InnerException.Message + <span class="cpp-string" nd="160">"\n"</span> + 
                            exp.InnerException.StackTrace, <span class="cpp-string">"Error"</span> ) ;
        }
    }
    <span class="cs-keyword">finally</span>
    {
        Console.Error.Flush() ;
        Console.Out.Flush() ;
    }

    <span class="cs-keyword">return</span> result ;
}
</pre>
		<h2>What about console events?</h2>
		<p>Okay, this was a pain in the rear to implement, but console events, generally CTRL-C, is used to interact with the process. When remoting the process, these events need to be remoted as well. There is a background thread on each of the machines that waits for events from the server. The console application traps any console events, passes them in parallel to each remote application, and regenerates those events on the remote machine. This is particularly painful since this is all being run underneath a service. By default, services don't have consoles associated with them. Well, one might claim this is a fairly simple endeavor, simply create a console with <code>AllocConsole()</code> and move on. It just so happens that child processes share the same console as the parent process. In this scenario, the temporary service shares the console with the child process performing the work. If we generate the console event, then it will actually send it to the service as well. So in order to handle all of this, the service itself will not respond to any console events (i.e., CTRL-C, CTRL-BREAK, etc.). This is not a big deal since we don't want the user interacting with this process anyway. None-the-less, you'll see a <code>DoNothithWithConsoleEvent()</code> handler routine that has a fairly self explanatory name.</p>
		<h2>"Confession is the road to healing..." (DC Talk, circa 1994)</h2>
		<p>Now, at this point, some of the dirty laundry must be aired in order for my conscience to feel good. There are obviously some security ramifications of this process as well as the fact that it doesn't handle any PInvoked methods via the loading mechanism. We'll tackle each of these subjects one at a time:</p>
		<h3>Security??!!</h3>
		<p>Security, you say? Well, first I should mention that in order to copy files into the <i>Admin$</i> share of the remote machine, you must be in Administrators group of the remote machine. Therefore, by default, hopefully, not everyone in the world is an Administrator of the remote machine. This is enforced by Windows default NTFS permissions.</p>
		<p>The second security issue is the identity of which the remote process runs. Since the process is running as a service, it is running under the "LOCALSYSTEM" account. I can hear the gaffe all the way to Dublin, CA. Running code under this account can be problematic in the sense that the "NT AUTHORITY\SYSTEM" account can do just about everything on the remote system. If your code does something that you didn't want it to do, you'll have to live with the consequences. I did look into the ability to impersonate the current user on the source machine in the process on the remote machine, and it is all feasible, but beyond my current allotment of time for this article. If you have some time and would like to investigate it, refer to the <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/remsspi.asp" target="_blank">article on MSDN</a>.</p>
		<p>The third and rather more obscure security issue is the fact that there isn't any authentication between the remote and source machine in the sense that the whole process could be liable to a man-in-the-middle attack. As well, there are moderate ways to transparently resolve this but they were beyond the time allotted for this article.</p>
		<p>On the other hand, I have in mind to integrate some 3rd party components to solve this problem such as the <a href="http://www.genuinechannels.com/">Genuine Channels</a> components to handle all the remoting infrastructure. Keep posted for more details.</p>
		<h3>Managing the Unmanaged</h3>
		<p>Gee, doesn't that title sound like something from a management seminar: "I will manage the unmanaged masses!" Anyway, there is a chance that you may have unmanaged method calls in your assembly. These calls will not proxy over the libraries from the source machine, but rather call the library from the local machine. If you're calling one of the system libraries, then this is actually a good thing. However, if you're making a call to a custom or third party unmanaged library, you must make sure it exists on the remote machine first. The only caveat to this is that there is an option to send over the unmanaged DLLs or any necesary files that are needed for your project. First zip them up into a zip file, then on the command line specify the file name with the argument:</p>
		<blockquote style="MARGIN-RIGHT: 0px">
				<p>-unmanaged myFiles.zip</p>
				<p>... Or if your calling this method programatically, specify this file in the ExecRemote command under the "unmanaged" parameter:</p>
				<div class="precollapse" id="premain7" style="WIDTH: 100%">
						<img id="preimg7" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="7" />
						<span id="precollapse7" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="7"> Collapse</span>
				</div>
				<pre lang="cs" id="pre7" style="MARGIN-TOP: 0px">
						<span class="cs-keyword">public</span>
						<span class="cs-keyword">int</span> ExecuteRemotely(
            <span class="cs-keyword">string</span>                      assemblyToRun,
            <span class="cs-keyword">string</span>[]                    argsForMain,
            <span class="cs-keyword">string</span>[]                    machineNames,
            <span class="cs-keyword">int</span>                         port,    
            <span class="cs-keyword">int</span>                         debugLevel,
            <span class="cs-keyword">string</span>                      remoteUserId,
            <span class="cs-keyword">string</span>                      remotePassword,
            <span class="cs-keyword">bool</span>                        interactive,
            TimeSpan                    allowedTime,
            <span class="cs-keyword">int</span>                         numberOfThreads,
            <span class="cs-keyword">string</span>                      unmanagedDependencies,
            <span class="cs-keyword">string</span>                      outputFolder,
            TextWriter                  consoleStdOut,
            TextWriter                  consoleStdErr,
            TextReader                  consoleStdIn,
            BeforeRunOnMachineDelegate  optionalBeforeHandler,
            AfterRunOnMachineDelegate   optionalAfterHandler,
            <span class="cs-keyword">object</span>                      optionalSource )
        </pre>
		</blockquote>
		<h3>Nothin' but .NET (or lack thereof)</h3>
		<p>Lastly, if .NET is not installed on the remote machine, the process will fail. The correct version of the framework must be installed on the remote machine for the code to run. Again, I considered a loader that would detect if the correct version of .NET was installed and install it if it wasn't there; all of which is possible. Yet another thing I didn't exactly have time for :) However, I have in mind a bootstrapper project that will install .NET on the remote machine before the shim process is run. Keep posted for more details.</p>
		<h2>Disclaimer</h2>
		<p>This article and the accompanying code are provided as-is. You may use it as you please. You may <b>not</b> hold me liable for any damage caused to you, your company, your neighbors or anyone else, as a result of reading this article or using the code. Whatever you do with this article and the accompanying code is at your own risk.</p>
		<h2>Version History</h2>
		<p>1.3 - Feb 28th, 2005</p>
		<ul>
				<li>Added ability to send unmanaged code / resources to the remote machine. Basically, you can send legacy DLLs or any other resouce (ie plain old files) to the remote machine. All you need to do is zip up all the resources and supply a zip file on the argument "-unmanaged myfiles.zip" to the command line or to the "unmanaged" argument for ExecRemote() method. 
</li>
				<li>Added ability to pull back any modified files on the remote machine. If you modify any files during the processing of your code, then those files can be pulled back and placed in a directory as the same name of the remote machine. 
</li>
				<li>Added a couple of extra checks to make sure that the current user has Administrative access to the remote machine and the remote resources are cleaned up in the end. Special thanks to the people at ICSharpCode.net for this functionality (<a href="http://www.icsharpcode.net/">http://www.icsharpcode.net/</a>) 
</li>
				<li>Added ability to redirect the output / input of the remote machine to a custom TextWriter / TextReader. A number of people had requested this feature, so I finally added it to the main method. However, by default, all the remote output/input are redirected to/from Console.StdOut, Console.StdIn, Console.StdErr. 
</li>
				<li>Fixed problem of running process on a source machine with multiple IP addresses. When using remoting and TCP, the source machine uses the "first" avilable IP address on that machine. For example, if you have two IP addresses "192.168.0.14" and "10.11.0.5", and your network is generally running on the 10.x.x.x network, the Uri for the remoting object is actually given out as tcp://192.168.0.14/YourRemoteObject.rem. I had a hard time believing this until I actually saw it in the debugger, but you can check out the article at <a href="http://www.glacialcomponents.com/ArticleDetail/CAOMN.aspx">http://www.glacialcomponents.com/ArticleDetail/CAOMN.aspx</a> for more details.</li>
		</ul>
		<p>1.2 - Jan 22nd, 2005</p>
		<ul>
				<li>Added ability to use a .NET configuration file that will be remoted (ie YourApp.exe.config will get proxied over) 
</li>
				<li>Added ability to use a license file (ie license.txt) 
</li>
				<li>Fixed problem with launching a program that defined main as Main() instead of Main( string[] args ). It will now function either way. 
</li>
				<li>All user assembly code is now loaded in a sepate AppDomain in the child process</li>
		</ul>
		<p>1.1 - Dec 29<sup>th</sup>, 2004</p>
		<ul>
				<li>Added usage for '-interactive' to allow application to interact with the desktop. 
</li>
				<li>Added the ability to issue a CTRL-C, CTRL-BREAK, etc. which will be echoed to all the remote applications. 
</li>
				<li>Added ability to use wildcards for the machine names(s) (e.g., <i>\\testwin*</i> will match <i>\\testwin2k</i> and <i>\\testwinxp</i> via LDAP lookup). 
</li>
				<li>Added ability to timeout on the remote application. 
</li>
				<li>Created a more useful demonstration application, <i>quickdir.exe</i>. This application will basically do a "dir" on the remote machine. </li>
		</ul>
		<p>1.0 - Dec 10<sup>th</sup>, 2004</p>
		<ul>
				<li>Initial version. </li>
		</ul>
		<h2>Special Recognition</h2>
		<p>I would like to thank <a href="mailto:mark@sysinternals.com">Mark Russinovich</a> for his article on <a href="http://www.sysinternals.com/ntw2k/freeware/psexec.shtml" target="_blank">Psexec</a> which inspired me to write this article. As well, I'd like to thank Suzanne Cook for her <a href="http://blogs.msdn.com/suzcook" target="_blank">blogs</a> on the .NET runtime loader which was a good reference for some very difficult issues. Lastly, but not least, I'd like to thank everyone who contributes to <a href="http://pinvoke.net/" target="_blank">PInvoke.net</a> from which I get most of my PInvoke method definitions (such as those to create services on Windows).</p>
		<h2>In Memory of Don Langewisch</h2>
		<p>I'd like to humbly dedicate this article to the memory of Don Langewisch (1955? - Dec. 10<sup>th</sup>, 2004). I received the news of his passing while finalizing this article. Don, a loving and dedicated man of God leaves behind a wife, two daughters and a son.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>About Jim Wiese (aka Spunk)</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">I generally attend most of the Microsoft DevDays in the south bay area (CA) and BayArea.NET functions in case any of you attend those as well. I'm always up for a lively disucussion about new technologies in the industry, Microsoft or not. Send me a note if you attend!</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/23922.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-12 20:35 <a href="http://www.cnitblog.com/MartinYao/articles/23922.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改IIS目录的Asp.Net版本 </title><link>http://www.cnitblog.com/MartinYao/articles/21924.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 16 Jan 2007 08:24:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21924.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21924.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21924.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21924.html</trackback:ping><description><![CDATA[本文将介绍一个方法用于修改虚拟目录的Asp.Net版本.
<p style="FONT-SIZE: 8pt"><strong>ASP.NET IIS 注册工具</strong></p><p style="FONT-SIZE: 8pt">使用ASP.NET IIS 注册工具 (Aspnet_regiis.exe)可以方便地更新 ASP.NET 应用程序的脚本映射，使其指向与该工具关联的 ASP.NET ISAPI 版本.<br />关于ASP.NET IIS 注册工具的更详细的内容,请参考MSDN.<br />在控制台上我们使用下面的命令可以修改一个虚拟目录的Asp.Net版本:<br /><br /><span style="FONT-SIZE: 14pt">Aspnet_iis.exe –s path</span></p><p style="FONT-SIZE: 8pt">我们知道了如何来修改一个虚拟目录的版本,现在的问题就是如何使用程序来实现它了.</p><p style="FONT-SIZE: 8pt">以下代码基于.Net FrameWork 2.0 在Windows Xp sp2中编译通过:<br /></p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">//</span><span style="COLOR: #008000">创建一个虚拟目录</span><span style="COLOR: #008000"><br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">            DirectoryEntry dirRoot </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> DirectoryEntry(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">IIS://localhost/W3SVC/1/Root</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            DirectoryEntries dirs </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> dirRoot.Children;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            DirectoryEntry virtualDir </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> dirs.Add(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">VirtualChange</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, dirRoot.SchemaClassName);<br /><img id="Codehighlighter1_287_294_Open_Image" onclick="this.style.display='none'; Codehighlighter1_287_294_Open_Text.style.display='none'; Codehighlighter1_287_294_Closed_Image.style.display='inline'; Codehighlighter1_287_294_Closed_Text.style.display='inline';" src="http://czhenq.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_287_294_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_287_294_Closed_Text.style.display='none'; Codehighlighter1_287_294_Open_Image.style.display='inline'; Codehighlighter1_287_294_Open_Text.style.display='inline';" src="http://czhenq.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top" />            object[] objs </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> object[] </span><span id="Codehighlighter1_287_294_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cnblogs.com/Images/dot.gif" /></span><span id="Codehighlighter1_287_294_Open_Text"><span style="COLOR: #000000">{ </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000"> }</span></span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            virtualDir.Invoke(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">AppCreate</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, objs);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            virtualDir.Properties[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">AppFriendlyName</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">][</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">] </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">VirtualChange</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            virtualDir.Properties[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Path</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">].Value </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">C:\\VirtualChange</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            virtualDir.CommitChanges();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">启动aspnet_iis.exe程序</span><span style="COLOR: #008000"><br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">            string fileName </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Environment.GetEnvironmentVariable(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">windir</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> @</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">\Microsoft.NET\Framework\v1.1.4322\aspnet_regiis.exe</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            ProcessStartInfo startInfo </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ProcessStartInfo(fileName);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">处理目录路径</span><span style="COLOR: #008000"><br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">            string path </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> virtualDir.Path.ToUpper();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> index </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> path.IndexOf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">W3SVC</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            path </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> path.Remove(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">, index);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">启动aspnet_iis.exe程序,刷新教本映射</span><span style="COLOR: #008000"><br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">            startInfo.Arguments </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">-s </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> path;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            startInfo.WindowStyle </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> ProcessWindowStyle.Hidden;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            startInfo.UseShellExecute </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            startInfo.CreateNoWindow </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            startInfo.RedirectStandardOutput </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            startInfo.RedirectStandardError </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            Process process </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Process();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            process.StartInfo </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> startInfo;<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            process.Start();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            process.WaitForExit();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            string errors </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> process.StandardError.ReadToEnd();<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (errors </span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000"> string.Empty)<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />                </span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Exception(errors);<br /><img src="http://czhenq.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />            Console.WriteLine(process.StandardOutput.ReadToEnd());</span></div><img src ="http://www.cnitblog.com/MartinYao/aggbug/21924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-16 16:24 <a href="http://www.cnitblog.com/MartinYao/articles/21924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Master Page Performance</title><link>http://www.cnitblog.com/MartinYao/articles/21775.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Thu, 11 Jan 2007 12:29:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21775.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21775.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21775.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21775.html</trackback:ping><description><![CDATA[
		<p>
				<strong>Q</strong>: I'm having a problem with performance in my ASP.NET master page. In the debugger I see the master page runs during each postback. How do we stop the master page from running its Load event each time? </p>
		<p>
				<strong>A</strong>: The MasterPage class derives from UserControl, which gives a hint as to why the master behaves this way. The master page becomes a control inside the page, and like every control in ASP.NET the master recreates itself on every postback (see <a href="http://odetocode.com/Articles/450.aspx">Master Pages: Tips, Tricks, and Traps</a>). </p>
		<p>You might be able to improve the situation using some <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondevelopinghigh-performanceaspnetapplications.asp">common ASP.NET performance tips</a>: 
</p>
		<ul>
				<li>Take areas of the master page that contain static content and place them into user controls. Place the user controls on the master page, and use <a href="http://www.asp.net/QuickStart/aspnet/doc/caching/default.aspx">output caching</a> to avoid executing the code inside. 
</li>
				<li>Check Page.IsPostBack during the master's Page_Load event handler and avoid performing unnecessary work. Use Atlas to refresh small sections of the page. 
</li>
				<li>Evaluate the use of ViewState. If you can let controls restore themselves from ViewState you can avoid re-querying the database. The tradeoff is increased page size, so measure carefully. </li>
		</ul>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-11 20:29 <a href="http://www.cnitblog.com/MartinYao/articles/21775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectoryServices.ActiveDirectory 技术摘要</title><link>http://www.cnitblog.com/MartinYao/articles/21429.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 02 Jan 2007 13:25:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21429.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21429.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21429.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21429.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21429.html</trackback:ping><description><![CDATA[
		<p>
				<a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl02',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory(VS.80).aspx">System.DirectoryServices.ActiveDirectory</a> 是 .NET Framework 的一个组件，它使程序能够执行与分区、架构、复制、信任和 Active Directory 配置有关的 Active Directory 管理任务。</p>
		<p>
				<b>System.DirectoryServices.ActiveDirectory</b> 是对 Active Directory 和 ADAM 进行了简化的简单接口，对于应用程序开发人员和要自动化常见管理任务的系统管理员而言，十分有用。尽管此命名空间提供的某些功能通过现有的 <a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl03',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices(VS.80).aspx">System.DirectoryServices</a> 类也可以获得，但使用 <b>System.DirectoryServices.ActiveDirectory</b> 执行许多任务时，编写代码所需的行数将会减少，对 Active Directory 架构掌握程度的要求也有所降低。</p>
		<div class="MTPS_CollapsibleRegion">
				<script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl04db71a40,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl04img,";
]]&gt;</script>
				<a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl04db71a40','ctl00_LibFrame_ctl04img');" href="http://msdn2.microsoft.com/zh-cn/library/ms257192(VS.80).aspx#">
						<img class="CollapseRegionImg" id="ctl00_LibFrame_ctl04img" alt="关键字" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />关键字</a>
				<br />
				<div class="section" id="ctl00_LibFrame_ctl04db71a40" style="DISPLAY: block">
						<div id="ctl00_LibFrame_ctl04">
								<p>Active Directory Management, Active Directory Scripting, management application, Active Directory Schema, replication, forests, trusts, ADAM, Active Directory 管理, Active Directory 脚本, 管理应用程序, Active Directory 架构, 复制, 林, 信任</p>
						</div>
				</div>
		</div>
		<div class="MTPS_CollapsibleRegion">
				<script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl0520dcfb5,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl05img,";
]]&gt;</script>
				<a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl0520dcfb5','ctl00_LibFrame_ctl05img');" href="http://msdn2.microsoft.com/zh-cn/library/ms257192(VS.80).aspx#">
						<img class="CollapseRegionImg" id="ctl00_LibFrame_ctl05img" alt="命名空间" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />命名空间</a>
				<br />
				<div class="section" id="ctl00_LibFrame_ctl0520dcfb5" style="DISPLAY: block">
						<div id="ctl00_LibFrame_ctl05">
								<p>
										<b>System.DirectoryServices.ActiveDirectory</b>
								</p>
						</div>
				</div>
		</div>
		<div class="MTPS_CollapsibleRegion">
				<script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl0632ac49c,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl06img,";
]]&gt;</script>
				<a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl0632ac49c','ctl00_LibFrame_ctl06img');" href="http://msdn2.microsoft.com/zh-cn/library/ms257192(VS.80).aspx#">
						<img class="CollapseRegionImg" id="ctl00_LibFrame_ctl06img" alt="相关技术" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />相关技术</a>
				<br />
				<div class="section" id="ctl00_LibFrame_ctl0632ac49c" style="DISPLAY: block">
						<div id="ctl00_LibFrame_ctl06">
								<p>
										<b>System.DirectoryServices</b>
								</p>
								<p>用 <b>System.DirectoryServices.ActiveDirectory</b> 对象实现的许多任务同样也可以用 <b>System.DirectoryServices</b> 对象实现。<b>System.DirectoryServices</b> 中的类提供对各种目录服务（包括 Active Directory、基于 LDAP 的目录、SAM、Novell Netware 和 IIS 目录）的一般性访问。<b>System.DirectoryServices.ActiveDirectory</b> 提供一个简化的模型，用于访问和处理特定于 Active Directory 的常用对象，例如域、架构属性、信任关系和复制计划。在 <b>System.DirectoryServices</b> 中不支持 <b>System.DirectoryServices.ActiveDirectory</b> 所提供的很大一部分功能。由于 <b>System.DirectoryServices</b> 的不适应特殊环境的性质，即使在 <b>System.DirectoryServices</b> 确实支持等效功能时，应用程序开发人员也必须编写更多的代码来执行相同的操作。</p>
								<p>
								</p>
								<p>
										<a onclick="javascript:Track('ctl00_LibFrame_ctl06|ctl00_LibFrame_ctl07',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.protocols(VS.80).aspx">System.DirectoryServices.Protocols</a>
								</p>
								<p>
										<b>System.DirectoryServices.Protocols</b> 命名空间通过轻型目录访问协议 (LDAP) 版本 3.0 (V3) 和目录服务标记语言 (DSML) 版本 2.0 (V2) 标准，提供对目录服务的以编程方式进行的一般性访问和管理。<b>System.DirectoryServices.Protocols</b> 为托管 LDAP 编程提供最高级别的控制和最高的性能。与 <b>System.DirectoryServices</b> 一样，<b>System.DirectoryServices.Protocols</b> 不是专为 Active Directory 和 ADAM 任务而设计的。 </p>
								<p>用于 Windows 的 DSML 服务</p>
								<p>通过 Windows 的目录服务标记语言 (DSML) 服务，应用程序能够使用 XML 文档通过 SOAP-DSML 消息对 Active Directory 执行读写操作。在使用此技术时，将采用 MS XML 对象之类的 COM 对象来处理消息。对于使用 .NET 本机接口的 DSML 处理，使用 <b>System.DirectoryServices.Protocols</b> 命名空间中的接口。有关用于 Windows 的 DSML 服务的详细信息，请参阅 MSDN Library（网址为 <a onclick="javascript:Track('ctl00_LibFrame_ctl06|ctl00_LibFrame_ctl08',this);" href="http://msdn.microsoft.com/library">http://msdn.microsoft.com/library</a>）中“Win32 和 COM 开发 SDK 文档”副标题下“目录服务”中的“用于 Windows 的 DSML 服务”。</p>
								<p>Active Directory COM 接口和 Win32 函数</p>
								<p>通过 Active Directory COM 接口和 Win32 函数，基于 Win32 的程序能够访问特定于 Active Directory 的管理对象并与 MMC Active Directory 管理单元集成。这些技术在非常低的目录级别与 Active Directory 交互，因此，与使用 <b>System.DirectoryServices</b> 和 <b>System.DirectoryServices.ActiveDirectory</b> 命名空间完成相同任务所需的编程步骤相比，上述技术通常需要进行更复杂的编程。它们还提供托管类未提供的一些功能，因此，在实现某些类型的应用（例如向 MMC 管理单元页添加新内容）时，必须使用 Active Directory COM 接口和 Win32 函数。有关这些技术的详细信息，请参阅 MSDN Library（网址为 <a onclick="javascript:Track('ctl00_LibFrame_ctl06|ctl00_LibFrame_ctl09',this);" href="http://msdn.microsoft.com/library">http://msdn.microsoft.com/library</a>）中“Win32 和 COM 开发 SDK 文档”副标题下“目录服务”中的“Active Directory”。</p>
								<p>ADSI</p>
								<p>Active Directory 服务接口 (ADSI) 是一组自动化和非自动化 COM 接口，提供对特定于 Active Directory 的管理对象、应用程序对象和 Active Directory 架构的访问。ADSI 中的自动化接口可用于编写脚本。C++、C# 和 VB.Net 等也支持非自动化 COM 接口的语言可以使用这两种类型的接口开发应用程序。有关 ADSI 的详细信息，请参阅 MSDN Library（网址为 <a onclick="javascript:Track('ctl00_LibFrame_ctl06|ctl00_LibFrame_ctl10',this);" href="http://msdn.microsoft.com/library">http://msdn.microsoft.com/library</a>）中“Win32 和 COM 开发 SDK 文档”副标题下“目录服务”中的“Active Directory 服务接口”。</p>
								<p>LDAP</p>
								<p>轻型目录访问协议 (LDAP) API 提供对基于 LDAP 的目录（包括 Active Directory）的访问。它使用标准 LDAP 协议访问、搜索和处理目录项。与 Active Directory COM 接口和 Win32 函数类似，LDAP 使开发人员能够通过其目录路径访问对象。但是，由于 LDAP 本身不能识别 Active Directory 的特定结构，因此，与其他 Active Directory 应用程序开发方法相比，基于 LDAP 的 Active Directory 应用程序通常更复杂且涉及更多的代码。有关 LDAP 的详细信息，请参阅 MSDN Library（网址为 <a onclick="javascript:Track('ctl00_LibFrame_ctl06|ctl00_LibFrame_ctl11',this);" href="http://msdn.microsoft.com/library">http://msdn.microsoft.com/library</a>）中“Win32 和 COM 开发 SDK 文档”副标题下“目录服务”中的“轻型目录访问协议”。</p>
						</div>
				</div>
		</div>
		<div class="MTPS_CollapsibleRegion">
				<script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl12c9eb273,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl12img,";
]]&gt;</script>
				<a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl12c9eb273','ctl00_LibFrame_ctl12img');" href="http://msdn2.microsoft.com/zh-cn/library/ms257192(VS.80).aspx#">
						<img class="CollapseRegionImg" id="ctl00_LibFrame_ctl12img" alt="背景" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />背景</a>
				<br />
				<div class="section" id="ctl00_LibFrame_ctl12c9eb273" style="DISPLAY: block">
						<div id="ctl00_LibFrame_ctl12">
								<p>
										<b>System.DirectoryServices.ActiveDirectory</b> 命名空间包含一组 .NET 类，这些类可以简化管理和访问 Active Directory 内容的应用程序的开发过程。</p>
								<p>通过 .NET Framework 的早期版本，应用程序开发人员能够使用 <b>System.DirectoryServices</b> 命名空间访问 Active Directory 项。但是，由于已开发了 <b>System.DirectoryServices</b> 以用于提供对多种不同类型的目录服务（而不只限于 Active Directory）的访问，因此，它并不包含提供对特定于 Active Directory 和 ADAM 的功能（例如由林和域组成的分区以及由站点、子网和站点链接组成的拓扑）的直接访问的类。如果开发人员知道相关的元素的路径并且正确构造了表示该路径的字符串，同时所有对象都存储于 Active Directory 中，但 <b>System.DirectoryServices</b> 命名空间本身并不清楚 Active Directory 的结构，则可以通过 <b>System.DirectoryServices</b> 访问这些元素。此外，有时必须检索或更新两个或多个元素，才能实现 Active Directory 管理任务。</p>
								<p>在许多组织中，对目录服务的 Active Directory 的依赖性日益增强，因此，应用程序开发人员需要一种更简单的方式访问和更新拓扑信息并控制 Active Directory 进程。这一新机制将只要求对 Active Directory 架构的内部结构有最基本的了解，并使开发人员通过尽可能少的应用程序代码行即可执行与 Active Directory 相关的常见任务。<b>System.DirectoryServices.ActiveDirectory</b> 就是为满足这些需求而设计的。</p>
								<p>使用 .NET Framework 编写的应用程序可以使用 <b>System.DirectoryServices.ActiveDirectory</b> 类（例如 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl13',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory.forest(VS.80).aspx">Forest</a>、<a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl14',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory.domain(VS.80).aspx">Domain</a> 和 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl15',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory.activedirectorysitelink(VS.80).aspx">ActiveDirectorySiteLink</a>）来枚举和浏览 Active Directory 树的拓扑。为了使用 <b>System.DirectoryServices.ActiveDirectory</b> 类引用对象，应用程序开发人员只需要知道该对象在层次结构中的位置，例如名为“sales”的域位于“contoso.com”林中。 类似地，开发人员可以通过 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl16',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory.replicationoperation(VS.80).aspx">ReplicationOperation</a> 之类的类检查和控制对 Active Directory 的操作的进度，或者通过 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl17',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.activedirectory.activedirectoryschema(VS.80).aspx">ActiveDirectorySchema</a> 之类的类查询或修改 Active Directory 架构本身。</p>
								<p>开发人员有时可能需要访问通过 <b>System.DirectoryServices</b> 提供的附加功能，同时还要利用 <b>System.DirectoryServices.ActiveDirectory</b> 类来导航 Active Directory 拓扑。为了实现此目的，应用程序通常可以获取对实际 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl18',this);" href="http://msdn2.microsoft.com/zh-cn/library/system.directoryservices.directoryentry(VS.80).aspx">DirectoryEntry</a> 对象的引用，该对象对应于 <b>System.DirectoryServices.ActiveDirectory</b> 命名空间中开发人员感兴趣的某个对象。然后，应用程序可以使用 <b>DirectoryEntry</b> 对象访问通过 <b>System.DirectoryServices</b> 命名空间中的类提供的功能，例如读取和写入 Active Directory 项安全描述符。</p>
								<p>有关 <b>System.DirectoryServices</b> 和 <b>System.DirectoryServices.ActiveDirectory</b> 命名空间的详细信息，请参阅 <a onclick="javascript:Track('ctl00_LibFrame_ctl12|ctl00_LibFrame_ctl19',this);" href="http://msdn2.microsoft.com/zh-cn/library/d11h6832(VS.80).aspx">.NET Framework 类库参考</a>。.NET Framework 还包含常规的 .NET Framework 编程信息。 </p>
						</div>
				</div>
		</div>
		<div class="MTPS_CollapsibleRegion">
				<script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl20eaca4ac,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl20img,";
]]&gt;</script>
				<a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl20eaca4ac','ctl00_LibFrame_ctl20img');" href="http://msdn2.microsoft.com/zh-cn/library/ms257192(VS.80).aspx#">
						<img class="CollapseRegionImg" id="ctl00_LibFrame_ctl20img" alt="新增功能" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />新增功能</a>
				<br />
				<div class="section" id="ctl00_LibFrame_ctl20eaca4ac" style="DISPLAY: block">
						<div id="ctl00_LibFrame_ctl20">
								<p>
										<b>System.DirectoryServices.ActiveDirectory</b> 是在 Visual Studio 2005 中引入的新的基于任务的类层次结构，它简化了向应用程序添加与 Active Directory 和 ADAM 相关的功能的过程。</p>
						</div>
				</div>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21429.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-02 21:25 <a href="http://www.cnitblog.com/MartinYao/articles/21429.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>controlling print jobs using WMI</title><link>http://www.cnitblog.com/MartinYao/articles/21319.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Fri, 29 Dec 2006 14:06:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21319.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21319.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21319.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21319.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21319.html</trackback:ping><description><![CDATA[
		<h2>Introduction</h2>
		<p>This article presents a simplified approach of controlling print jobs using WMI. To know more about WMI, please visit <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/about_wmi.asp" target="_blank">MSDN</a>.</p>
		<p>Why WMI? Because it provides a simplified approach and avoids making API calls within our C# code. WMI comes as default for Windows XP and Windows 2000. For Windows 95/98, we need to download WMI Core 1.5 and install it: <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=98A4C5BA-337B-4E92-8C18-A63847760EA5&amp;displaylang=en" target="_blank">WMI Core 1.5</a>.</p>
		<h2>The Approach</h2>
		<p>First, let's see how to get the list of printers.</p>
		<pre lang="cs">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">static</span> StringCollection GetPrintersCollection()
{
    StringCollection printerNameCollection = <span class="cs-keyword">new</span> StringCollection();
    <span class="cs-keyword">string</span> searchQuery = <span class="cpp-string">"SELECT * FROM Win32_Printer"</span>;
    ManagementObjectSearcher searchPrinters = 
          <span class="cs-keyword">new</span> ManagementObjectSearcher(searchQuery);
    ManagementObjectCollection printerCollection = searchPrinters.Get();
    <span class="cs-keyword">foreach</span>(ManagementObject printer <span class="cs-keyword">in</span> printerCollection)
    {
        printerNameCollection.Add(printer.Properties[<span class="cpp-string">"Name"</span>].Value.ToString());
    }
    <span class="cs-keyword">return</span> printerNameCollection;
}</pre>
		<p>The above method returns the list of printers configured in the local machine. The printer <code>ManagementObject</code> exposes many useful properties. Using the <code>PaperSizesSupported</code> property, we can get the list of paper sizes supported by that particular printer. To view information about <code>Win32_Printer</code>, please refer <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/win32_printer.asp" target="_blank">MSDN</a>.</p>
		<p>Using this printer name, we can fetch the print job collection by using the following method:</p>
		<div class="precollapse" id="premain1" style="WIDTH: 100%">
				<img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" />
				<span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="1"> Collapse</span>
		</div>
		<pre lang="cs" id="pre1" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">static</span> StringCollection GetPrintJobsCollection(<span class="cs-keyword">string</span> printerName)
{
  StringCollection printJobCollection = <span class="cs-keyword">new</span> StringCollection();
  <span class="cs-keyword">string</span> searchQuery = <span class="cpp-string">"SELECT * FROM Win32_PrintJob"</span>;
  
  <span class="cs-comment">/*searchQuery can also be mentioned with where Attribute,
      but this is not working in Windows 2000 / ME / 98 machines 
      and throws Invalid query error*/</span>
  ManagementObjectSearcher searchPrintJobs = 
            <span class="cs-keyword">new</span> ManagementObjectSearcher(searchQuery);
  ManagementObjectCollection prntJobCollection = searchPrintJobs.Get();
  <span class="cs-keyword">foreach</span>(ManagementObject prntJob <span class="cs-keyword">in</span> prntJobCollection)
  {
    System.String jobName = prntJob.Properties[<span class="cpp-string">"Name"</span>].Value.ToString();

    <span class="cs-comment">//Job name would be of the format [Printer name], [Job ID]</span><span class="cs-keyword">char</span>[] splitArr = <span class="cs-keyword">new</span><span class="cs-keyword">char</span>[<span class="cs-literal">1</span>];
    splitArr[<span class="cs-literal">0</span>] = Convert.ToChar(<span class="cpp-string">","</span>);
    <span class="cs-keyword">string</span> prnterName = jobName.Split(splitArr)[<span class="cs-literal">0</span>];
    <span class="cs-keyword">string</span> documentName = prntJob.Properties[<span class="cpp-string">"Document"</span>].Value.ToString();
    <span class="cs-keyword">if</span>(String.Compare(prnterName, printerName, <span class="cs-keyword">true</span>) == <span class="cs-literal">0</span>)
    {
      printJobCollection.Add(documentName);
    }
  }
  <span class="cs-keyword">return</span> printJobCollection;
}</pre>
		<p>The query "<code lang="sql"><span class="vb-function">SELECT</span> * <span class="vb-function">FROM</span> Win32_PrintJob</code>" can also be used as <code lang="cs"><span class="cpp-string">"SELECT * FROM Win32_PrintJob WHERE Name like '"</span>+ printerName.Replace(<span class="cpp-string">"\"</span>, <span class="cpp-string">"\\"</span>) +<span class="cpp-string">"%'"</span></code>. But the query with <code lang="sql">WHICH</code> attribute caused problems in my system which was running Windows 2000, but ran smooth in Windows XP machines. So currently, I am making a loop through all the print jobs and identify print jobs for that particular printer.</p>
		<p>Now, let's see how to manage these print jobs. The print console provided by Windows allows us to Pause, Resume and Cancel print jobs. It also allows to set priority for a print job. Using WMI, we can perform Pause, Resume and Cancel, but it doesn't provide any method for changing the priority level.</p>
		<p>The following code depicts how to pause a print job:</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="2"> Collapse</span>
		</div>
		<pre lang="cs" id="pre2" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">static</span>
				<span class="cs-keyword">bool</span> PausePrintJob(<span class="cs-keyword">string</span> printerName, <span class="cs-keyword">int</span> printJobID)
{
  <span class="cs-keyword">bool</span> isActionPerformed = <span class="cs-keyword">false</span>;
  <span class="cs-keyword">string</span> searchQuery = <span class="cpp-string">"SELECT * FROM Win32_PrintJob"</span>;
  ManagementObjectSearcher searchPrintJobs = 
           <span class="cs-keyword">new</span> ManagementObjectSearcher(searchQuery);
  ManagementObjectCollection prntJobCollection = searchPrintJobs.Get();
  <span class="cs-keyword">foreach</span>(ManagementObject prntJob <span class="cs-keyword">in</span> prntJobCollection)
  {
    System.String jobName = prntJob.Properties[<span class="cpp-string">"Name"</span>].Value.ToString();
    <span class="cs-comment">//Job name would be of the format [Printer name], [Job ID]</span><span class="cs-keyword">char</span>[] splitArr = <span class="cs-keyword">new</span><span class="cs-keyword">char</span>[<span class="cs-literal">1</span>];
    splitArr[<span class="cs-literal">0</span>] = Convert.ToChar(<span class="cpp-string">","</span>);
    <span class="cs-keyword">string</span> prnterName = jobName.Split(splitArr)[<span class="cs-literal">0</span>];
    <span class="cs-keyword">int</span> prntJobID = Convert.ToInt32(jobName.Split(splitArr)[<span class="cs-literal">1</span>]);
    <span class="cs-keyword">string</span> documentName = prntJob.Properties[<span class="cpp-string">"Document"</span>].Value.ToString();
    <span class="cs-keyword">if</span>(String.Compare(prnterName, printerName, <span class="cs-keyword">true</span>) == <span class="cs-literal">0</span>)
    {
      <span class="cs-keyword">if</span>(prntJobID == printJobID)
      {
        prntJob.InvokeMethod(<span class="cpp-string">"Pause"</span>, <span class="cs-keyword">null</span>);
        isActionPerformed = <span class="cs-keyword">true</span>; 
        <span class="cs-keyword">break</span>;
      }
    }
  }
  <span class="cs-keyword">return</span> isActionPerformed;
}</pre>
		<p>By invoking the <code>Pause</code> method, we can pause the print job. Similar approach is required for resuming the print job. Just we need to invoke the <code>Resume</code> method.</p>
		<pre lang="cs">prntJob.InvokeMethod(<span class="cpp-string">"Resume"</span>, <span class="cs-keyword">null</span>);</pre>
		<p>
				<code>Win32_PrintJob WMI_CLASS</code> provides methods to pause and resume a print job. But it doesn't provide any method for canceling the print job. To cancel the print job, you need to just delete the management object.</p>
		<div class="precollapse" id="premain4" style="WIDTH: 100%">
				<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
				<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="4"> Collapse</span>
		</div>
		<pre lang="cs" id="pre4" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">static</span>
				<span class="cs-keyword">bool</span> CancelPrintJob(<span class="cs-keyword">string</span> printerName, <span class="cs-keyword">int</span> printJobID)
{
  <span class="cs-keyword">bool</span> isActionPerformed = <span class="cs-keyword">false</span>;
  <span class="cs-keyword">string</span> searchQuery = <span class="cpp-string">"SELECT * FROM Win32_PrintJob"</span>;
  ManagementObjectSearcher searchPrintJobs = 
         <span class="cs-keyword">new</span> ManagementObjectSearcher(searchQuery);
  ManagementObjectCollection prntJobCollection = searchPrintJobs.Get();
  <span class="cs-keyword">foreach</span>(ManagementObject prntJob <span class="cs-keyword">in</span> prntJobCollection)
  {
    System.String jobName = prntJob.Properties[<span class="cpp-string">"Name"</span>].Value.ToString();
    <span class="cs-comment">//Job name would be of the format [Printer name], [Job ID]</span><span class="cs-keyword">char</span>[] splitArr = <span class="cs-keyword">new</span><span class="cs-keyword">char</span>[<span class="cs-literal">1</span>];
    splitArr[<span class="cs-literal">0</span>] = Convert.ToChar(<span class="cpp-string">","</span>);
    <span class="cs-keyword">string</span> prnterName = jobName.Split(splitArr)[<span class="cs-literal">0</span>];
    <span class="cs-keyword">int</span> prntJobID = Convert.ToInt32(jobName.Split(splitArr)[<span class="cs-literal">1</span>]);
    <span class="cs-keyword">string</span> documentName = prntJob.Properties[<span class="cpp-string">"Document"</span>].Value.ToString();
    <span class="cs-keyword">if</span>(String.Compare(prnterName, printerName, <span class="cs-keyword">true</span>) == <span class="cs-literal">0</span>)
    {
      <span class="cs-keyword">if</span>(prntJobID == printJobID)
      {
        <span class="cs-comment">//performs a action similar to the cancel </span><span class="cs-comment">//operation of windows print console</span>
        prntJob.Delete();
        isActionPerformed = <span class="cs-keyword">true</span>; 
        <span class="cs-keyword">break</span>;
      }
    }
  }
  <span class="cs-keyword">return</span> isActionPerformed;
}</pre>
		<p>That's it.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>mubbsher</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=115426">here</a> to view mubbsher's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21319.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2006-12-29 22:06 <a href="http://www.cnitblog.com/MartinYao/articles/21319.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>