﻿<?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>BlogJava-superwei-文章分类-DoNet</title><link>http://www.blogjava.net/superwei/category/7379.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 02 Jun 2012 02:33:30 GMT</lastBuildDate><pubDate>Sat, 02 Jun 2012 02:33:30 GMT</pubDate><ttl>60</ttl><item><title>C# 调用EXE,设置参数，并取得返回值</title><link>http://www.blogjava.net/superwei/articles/191660.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Wed, 09 Apr 2008 04:44:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/191660.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/191660.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/191660.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/191660.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/191660.html</trackback:ping><description><![CDATA[<p>以下代码的功能是在程序中调用EXE,并设置参数和取得返回值。留此代码只是为了留得一个纪念&nbsp;&nbsp;<br />
&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; string strExePath =&nbsp;带有EXE名字的完整路径；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessStartInfo procInfo = new ProcessStartInfo(strExePath, 传给EXE 的参数);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 隐藏EXE运行的窗口<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; procInfo.WindowStyle = ProcessWindowStyle.Hidden;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // exe运行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process procBatch = Process.Start(procInfo);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 取得EXE运行后的返回值，返回值只能是整型</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int iRtn = procBatch.ExitCode;&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; catch(Exception ex)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<img src ="http://www.blogjava.net/superwei/aggbug/191660.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2008-04-09 12:44 <a href="http://www.blogjava.net/superwei/articles/191660.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows2003--打造IIS网站的专用“护心甲”—实战SSL加密 </title><link>http://www.blogjava.net/superwei/articles/188440.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 25 Mar 2008 03:25:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/188440.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/188440.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/188440.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/188440.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/188440.html</trackback:ping><description><![CDATA[<a href="http://blog.csdn.net/taocsdn/archive/2004/07/12/39513.aspx">打造IIS网站的专用&#8220;护心甲&#8221;—实战SSL加密</a>&nbsp;&nbsp; <script src="http://blog.csdn.net/count.aspx?ID=39513&amp;Type=Rank"></script>
<div class="postText"><span style="font-size: 10pt">（载自：<a href="http://blog.csdn.net/taocsdn/archive/2004/07/12/39513.aspx">http://blog.csdn.net/taocsdn/archive/2004/07/12/39513.aspx</a>）<br />
</span>Windows网络操作系统内置的IIS是大家最常用的Web服务器。但在系统默认配置下，IIS使用的是&#8220;HTTP协议&#8221;以明文形式传输数据，没有采用任何加密手段，传输的重要数据很容易被窃取。这对于一些安全性要求高的网站来说，是远远不够的。为了保证重要数据的万无一失，IIS也提供了SSL安全加密机制，下面就向大家介绍如何在IIS服务器中使用SSL安全加密机制。
<p>　　笔者以Windows Server 2003(简称Windows 2003)系统为例，介绍如何在IIS6服务器中应用SSL安全加密机制功能。要想为某个IIS网站创建数字证书，首先必须使用&#8220;Web服务器证书向导&#8221;功能为该网站生成一个证书请求文件。进入&#8220;控制面板&#8594;管理工具&#8594;Internet 信息服务(IIS)管理器&#8221;，在IIS管理器窗口中展开&#8220;网站&#8221;目录，右键点击要使用SSL安全加密机制功能的网站，在弹出菜单中选择&#8220;属性&#8221;，然后切换到&#8220;目录安全性&#8221;标签页，接着点击&#8220;服务器证书&#8221;按钮。在&#8220;IIS证书向导&#8221;窗口中选择&#8220;新建证书&#8221;选项，点击&#8220;下一步&#8221;，选中&#8220;现在准备证书请求，但稍后发送&#8221;，接着在&#8220;名称&#8221;栏中为该证书起个名字，在&#8220;位长&#8221;下拉列表中选择&#8220;密钥的位长&#8221;，这里要注意，位长不能设置的过大，否则会影响通信质量；接着设置证书的单位、部门、和地理信息，在站点&#8220;公用名称栏&#8221;中输入该网站的域名，然后指定证书请求文件的保存位置，这里笔者将该证书请求文本文件保存在&#8220;d＼certreq.txt&#8221;。这样就完成了证书请求文件的生成。</p>
<p>　　申请IIS网站证书</p>
<p>　　完成了证书请求文件的生成后，就可以开始申请IIS网站证书了。但这个过程需要证书服务(Certificate Services)的支持。Windows 2003系统默认状态没安装此服务，需要手工添加。</p>
<p>　　● 安装证书服务</p>
<p>　　在&#8220;控制面板&#8221;中运行&#8220;添加或删除程序&#8221;，切换到&#8220;添加/删除Windows组件&#8221;页，在&#8220;Windows组件向导&#8221;对话框中，选中&#8220;证书服务&#8221;选项，接下来选择CA类型，这里笔者选择&#8220;独立根CA&#8221;，然后为该CA服务器起个名字，设置证书的有效期限，建议使用默认值&#8220;5年&#8221;即可，最后指定证书数据库和证书数据库日志的位置后，就完成了证书服务的安装。</p>
<p>　　完成了证书服务的安装后，就能开始申请IIS网站证书了。运行Internet Explorer浏览器，在地址栏中输入&#8220;localhost/CertSrv/default.asp&#8221;。接着在&#8220;Microsoft 证书服务&#8221;欢迎窗口中点击&#8220;申请一个证书&#8221;链接，然后在证书申请类型中点击&#8220;高级证书申请&#8221;链接，在高级证书申请窗口中点击&#8220;使用BASE64编码的CMC或PKCS＃10文件提交&#8230;.&#8221;链接，接着将证书请求文件的内容复制到&#8220;保存的申请&#8221;输入框中，这里笔者的证书请求文件内容保存在&#8220;d:\certreq.txt&#8221;，最后点击&#8220;提交&#8221;按钮。</p>
<p>　　颁发IIS网站证书</p>
<p>　　虽然完成了IIS网站证书的申请后，但这时它还处于挂起状态，需要颁发后才能生效。在&#8220;控制面板&#8594;管理工具&#8221;中，运行&#8220;证书颁发机构&#8221;程序。在&#8220;证书颁发机构&#8221;左侧窗口中展开目录，选中&#8220;挂起的申请&#8221;目录，在右侧窗口找到刚才申请的证书，鼠标右键点击该证书，选择&#8220;所有任务&#8594;颁发&#8221;。</p>
<p>　　接着点击 &#8220;颁发的证书&#8221;目录，打开刚刚颁发成功的证书，在 &#8220;证书&#8221;对话框中切换到&#8220;详细信息&#8221;标签页。点击&#8220;复制到文件&#8221;按钮，弹出证书导出对话框，一路下一步，在&#8220;要导出的文件&#8221;栏中指定文件名，这里笔者保存证书路径为&#8220;d:\cce.cer&#8221;，最后点击&#8220;完成&#8221;。</p>
<p>　　导入IIS网站证书</p>
<p>　　在IIS管理器的&#8220;目录安全性&#8221;标签页中，点击&#8220;服务器证书&#8221;按钮，这时弹出&#8220;挂起的证书请求&#8221;对话框，选择&#8220;处理挂起的请求并安装证书&#8221;选项，点击&#8220;下一步&#8221;后，指定好刚才导出的IIS网站证书文件的位置，接着指定SSL使用的端口，建议使用默认的&#8220;443&#8221;，最后点击&#8220;完成&#8221;按钮</p>
<p>　　配置IIS服务器</p>
<p>　　完成了证书的导入后，IIS网站这时还没有启用SSL安全加密功能，需要对IIS服务器进行配置。</p>
<p>　　在&#8220;目录安全性&#8221;标签页，点击安全通信栏的&#8220;编辑&#8221;按钮，选中&#8220;要求安全通道(SSL)&#8221;和&#8220;要求128位加密&#8221;选项，最后点击&#8220;确定&#8221;按钮即可。</p>
<p>　　接着点击&#8220;身份验证和访问控制&#8221;栏的&#8220;编辑&#8221;按钮，在对话框中取消&#8220;启用匿名访问&#8221;和&#8220;集成Windows身份验证&#8221;选项，这里要选中&#8220;基本身份验证&#8221;选项，最后点击&#8220;确定&#8221;按钮。</p>
<p align="center"><img src="http://image2.sina.com.cn/IT/c/2004-06-24/U571P2T1D379608F14DT20040624112229.jpg" border="1"  alt="" /><br />
图1</p>
<p align="left">　　Windows网络操作系统内置的IIS是大家最常用的Web服务器。但在系统默认配置下，IIS使用的是&#8220;HTTP协议&#8221;以明文形式传输数据，没有采用任何加密手段，传输的重要数据很容易被窃取。这对于一些安全性要求高的网站来说，是远远不够的。为了保证重要数据的万无一失，IIS也提供了SSL安全加密机制，下面就向大家介绍如何在IIS服务器中使用SSL安全加密机制。</p>
<p>　　笔者以Windows Server 2003(简称Windows 2003)系统为例，介绍如何在IIS6服务器中应用SSL安全加密机制功能。要想为某个IIS网站创建数字证书，首先必须使用&#8220;Web服务器证书向导&#8221;功能为该网站生成一个证书请求文件。进入&#8220;控制面板&#8594;管理工具&#8594;Internet 信息服务(IIS)管理器&#8221;，在IIS管理器窗口中展开&#8220;网站&#8221;目录，右键点击要使用SSL安全加密机制功能的网站，在弹出菜单中选择&#8220;属性&#8221;，然后切换到&#8220;目录安全性&#8221;标签页，接着点击&#8220;服务器证书&#8221;按钮。在&#8220;IIS证书向导&#8221;窗口中选择&#8220;新建证书&#8221;选项，点击&#8220;下一步&#8221;，选中&#8220;现在准备证书请求，但稍后发送&#8221;，接着在&#8220;名称&#8221;栏中为该证书起个名字，在&#8220;位长&#8221;下拉列表中选择&#8220;密钥的位长&#8221;，这里要注意，位长不能设置的过大，否则会影响通信质量；接着设置证书的单位、部门、和地理信息，在站点&#8220;公用名称栏&#8221;中输入该网站的域名，然后指定证书请求文件的保存位置，这里笔者将该证书请求文本文件保存在&#8220;d＼certreq.txt&#8221;。这样就完成了证书请求文件的生成。</p>
<p>　　申请IIS网站证书</p>
<p>　　完成了证书请求文件的生成后，就可以开始申请IIS网站证书了。但这个过程需要证书服务(Certificate Services)的支持。Windows 2003系统默认状态没安装此服务，需要手工添加。</p>
<p>　　● 安装证书服务</p>
<p>　　在&#8220;控制面板&#8221;中运行&#8220;添加或删除程序&#8221;，切换到&#8220;添加/删除Windows组件&#8221;页，在&#8220;Windows组件向导&#8221;对话框中，选中&#8220;证书服务&#8221;选项，接下来选择CA类型，这里笔者选择&#8220;独立根CA&#8221;，然后为该CA服务器起个名字，设置证书的有效期限，建议使用默认值&#8220;5年&#8221;即可，最后指定证书数据库和证书数据库日志的位置后，就完成了证书服务的安装。</p>
<p>　　完成了证书服务的安装后，就能开始申请IIS网站证书了。运行Internet Explorer浏览器，在地址栏中输入&#8220;localhost/CertSrv/default.asp&#8221;。接着在&#8220;Microsoft 证书服务&#8221;欢迎窗口中点击&#8220;申请一个证书&#8221;链接，然后在证书申请类型中点击&#8220;高级证书申请&#8221;链接，在高级证书申请窗口中点击&#8220;使用BASE64编码的CMC或PKCS＃10文件提交&#8230;.&#8221;链接，接着将证书请求文件的内容复制到&#8220;保存的申请&#8221;输入框中，这里笔者的证书请求文件内容保存在&#8220;d:\certreq.txt&#8221;，最后点击&#8220;提交&#8221;按钮。</p>
<p>　　颁发IIS网站证书</p>
<p>　　虽然完成了IIS网站证书的申请后，但这时它还处于挂起状态，需要颁发后才能生效。在&#8220;控制面板&#8594;管理工具&#8221;中，运行&#8220;证书颁发机构&#8221;程序。在&#8220;证书颁发机构&#8221;左侧窗口中展开目录，选中&#8220;挂起的申请&#8221;目录，在右侧窗口找到刚才申请的证书，鼠标右键点击该证书，选择&#8220;所有任务&#8594;颁发&#8221;。</p>
<p>　　接着点击 &#8220;颁发的证书&#8221;目录，打开刚刚颁发成功的证书，在 &#8220;证书&#8221;对话框中切换到&#8220;详细信息&#8221;标签页。点击&#8220;复制到文件&#8221;按钮，弹出证书导出对话框，一路下一步，在&#8220;要导出的文件&#8221;栏中指定文件名，这里笔者保存证书路径为&#8220;d:\cce.cer&#8221;，最后点击&#8220;完成&#8221;。</p>
<p>　　导入IIS网站证书</p>
<p>　　在IIS管理器的&#8220;目录安全性&#8221;标签页中，点击&#8220;服务器证书&#8221;按钮，这时弹出&#8220;挂起的证书请求&#8221;对话框，选择&#8220;处理挂起的请求并安装证书&#8221;选项，点击&#8220;下一步&#8221;后，指定好刚才导出的IIS网站证书文件的位置，接着指定SSL使用的端口，建议使用默认的&#8220;443&#8221;，最后点击&#8220;完成&#8221;按钮</p>
<p>　　配置IIS服务器</p>
<p>　　完成了证书的导入后，IIS网站这时还没有启用SSL安全加密功能，需要对IIS服务器进行配置。</p>
<p>　　在&#8220;目录安全性&#8221;标签页，点击安全通信栏的&#8220;编辑&#8221;按钮，选中&#8220;要求安全通道(SSL)&#8221;和&#8220;要求128位加密&#8221;选项，最后点击&#8220;确定&#8221;按钮即可。</p>
<p>　　接着点击&#8220;身份验证和访问控制&#8221;栏的&#8220;编辑&#8221;按钮，在对话框中取消&#8220;启用匿名访问&#8221;和&#8220;集成Windows身份验证&#8221;选项，这里要选中&#8220;基本身份验证&#8221;选项，最后点击&#8220;确定&#8221;按钮。</p>
<p align="center"><img src="http://image2.sina.com.cn/IT/c/2004-06-24/U571P2T1D379608F14DT20040624112229.jpg" border="1"  alt="" /><br />
图1</p>
<p align="left">　　SSL安全加密机制</p>
<p align="left">　　SSL(Security Socket Layer)的中文全称是&#8220;加密套接字协议层&#8221;，是由Netscape公司推出的一种安全通信协议，它位于HTTP协议层和TCP协议层之间，能够对信用卡和个人信息提供较强的保护。SSL在客户和服务器之间建立一条加密通道，确保所传输的数据不被非法窃取，SSL安全加密机制功能是依靠使用数字证书来实现的。</p>
<p align="left">　　应用了SSL加密机制后，IIS服务器的数据通信过程如下：首先客户端与IIS服务器建立通信连接，接着IIS把数字证书与公用密钥发给客户端。然后使用这个公共密钥对客户端的会话密钥进行加密后，传递给IIS服务器，服务器端接收后用私人密钥进行解密，这时就在客户端和IIS服务器间创建了一条安全数据通道，只有被IIS服务器允许的客户才能与它进行通信。机制</p>
<p align="left">　　SSL(Security Socket Layer)的中文全称是&#8220;加密套接字协议层&#8221;，是由Netscape公司推出的一种安全通信协议，它位于HTTP协议层和TCP协议层之间，能够对信用卡和个人信息提供较强的保护。SSL在客户和服务器之间建立一条加密通道，确保所传输的数据不被非法窃取，SSL安全加密机制功能是依靠使用数字证书来实现的。</p>
<p align="left">　　应用了SSL加密机制后，IIS服务器的数据通信过程如下：首先客户端与IIS服务器建立通信连接，接着IIS把数字证书与公用密钥发给客户端。然后使用这个公共密钥对客户端的会话密钥进行加密后，传递给IIS服务器，服务器端接收后用私人密钥进行解密，这时就在客户端和IIS服务器间创建了一条安全数据通道，只有被IIS服务器允许的客户才能与它进行通信。</p>
<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=39513</p>
</div>
<img src ="http://www.blogjava.net/superwei/aggbug/188440.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2008-03-25 11:25 <a href="http://www.blogjava.net/superwei/articles/188440.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在IIS中使用SSL配置HTTPS网站</title><link>http://www.blogjava.net/superwei/articles/188437.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 25 Mar 2008 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/188437.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/188437.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/188437.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/188437.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/188437.html</trackback:ping><description><![CDATA[<p><span lang="ZH-CN" style="font-size: 11pt; font-family: '新細明體','serif'; mso-fareast-language: ZH-CN; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt; mso-ansi-language: EN-US; mso-bidi-language: AR-SA"><font face="Arial"><strong>在IIS中使用SSL配置HTTPS网站<br />
</strong><span style="font-size: 10pt">(载自：http://blog.csdn.net/nj_1st_excellence/archive/2008/01/18/2051515.aspx)</span><br />
</font></span></p>
<p>&nbsp;&nbsp;&nbsp; 由于Windows系统的普及，很多中小企业在自己的网站和内部办公管理系统都是用默认的IIS来做WEB服务器使用。<br />
&nbsp;&nbsp;&nbsp; 默认情况下我们所使用的HTTP协议是没有任何加密措施的，所有的消息全部都是以明文形式在网络上传送的，恶意的攻击者可以通过安装监听程序来获得我们和服务器之间的通讯内容。这点危害在一些企业内部网络中尤其比较大，对于使用HUB的企业内网来说简直就是没有任何安全可讲因为任何人都可以在一台电脑上看到其他人在网络中的活动，对于使用交换机来组网的网络来说虽然安全威胁性要小很多，但很多时候还是会有安全突破口，比如没有更改交换机的默认用户和口令，被人上去把自己的网络接口设置为侦听口，依然可以监视整个网络的所有活动。<br />
&nbsp;&nbsp;&nbsp; IIS的身份认证除了匿名访问、基本验证和Windows NT请求/响应方式外，还有一种安全性更高的认证，就是通过SSL(Security Socket Layer)安全机制使用数字证书。<br />
&nbsp;&nbsp;&nbsp; 因此，为了网络的安全越来越多的企业采用SSL来避免或减少这方面带来的损失。<br />
&nbsp;&nbsp;&nbsp; SSL(加密套接字协议层)位于HTTP层和TCP层之间，建立用户与服务器之间的加密通信，确保所传递信息的安全性。SSL是工作在公共密钥和私人密钥基础上的，任何用户都可以获得公共密钥来加密数据，但解密数据必须要通过相应的私人密钥。使用SSL安全机制时，首先客户端与服务器建立连接，服务器把它的数字证书与公共密钥一并发送给客户端，客户端随机生成会话密钥，用从服务器得到的公共密钥对会话密钥进行加密，并把会话密钥在网络上传递给服务器，而会话密钥只有在服务器端用私人密钥才能解密，这样，客户端和服务器端就建立了一个惟一的安全通道。<br />
&nbsp;&nbsp;&nbsp; <strong>建立了SSL安全机制后，只有SSL允许的客户才能与SSL允许的Web站点进行通信，并且在使用URL资源定位器时，输入https:// ，而不是http://。</strong><br />
&nbsp;&nbsp;&nbsp; 下面我们以WIN2000服务器版本的来做例子，介绍一下怎样利用SSL加密HTTP通道来加强IIS安全的。</p>
<p>&nbsp;&nbsp;&nbsp; 操作办法</p>
<p>&nbsp;&nbsp;&nbsp; 我们首先需要在控制面板里的填加删除WINDOWS组件中去安装证书服务，这个服务在默认安装中是没有安装在系统里的，需要安装光盘来安装。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/1.jpg" /></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/2.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 然后选择独立根CA的安装类型。然后在下一步中给自己的CA起一个名字来完成安装就可以了。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 安装完成后，我们就可以启动我们的IIS管理器来申请一个数字证书了，启动INTERNET管理器选择我们需要配置的WEB站点</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/3.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 选择站点属性里的，目录安全性-安全通信-服务器证书</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/4.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 由于我们是第一次配置，所以选择创建一个新的证书。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/5.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 用默认的站点名称和加密位长设置就可以了。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/6.jpg" /></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/7.jpg" />&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/8.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 选择一个地方把我们刚才生成的一个请求证书保存起来。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/9.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 完成上面的设置后，我们就要把我们刚刚生成的服务器证书提交给我们刚刚在本地安装的证书服务器。在默认情况下证书服务器完成安装后会在本地的IIS里的WEB服务器里面生成几个虚拟的目录。</p>
<p>&nbsp;&nbsp;&nbsp; 我们打开<a href="http://localhost/CertSrv/default.asp">http://localhost/CertSrv/default.asp</a></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/10.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 选择申请证书</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/11.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 在选择申请类型的时候，选择高级申请。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/12.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 选择使用base64的编码方式来提交我们的证书申请。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/13.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 在证书申请的地方把我们刚刚生成的certreq.txt的内容拷备进去，然后选择提交。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/14.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 提交成功以后，会返回一个页面给我们告诉我们证书已经成功提交了，现在是挂起状态就是等待CA中心来颁发这个证书了。</p>
<p>&nbsp;&nbsp;&nbsp; 好接下来启动管理工具里的证书颁发机构，在待定申请中找到我们刚刚的申请条目然后点击鼠标右键选择颁发就好了。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/15.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 颁发成功以后我们在颁发的证书里找到刚才颁发的证书，双击其属性栏目然后在详细信息里选择将证书复制到文件。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/16.jpg" /></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/17.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 我们需要把证书导出到一个文件，这里我们把证书导出到c: \sql.cer这个文件里。</p>
<p>&nbsp;&nbsp;&nbsp; 重新回到IIS的WEB管理界面里重新选择证书申请，这个时候出来的界面就是挂起的证书请求了。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/18.jpg" /></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/19.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 选择我们导处的sql.cer这个文件。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/20.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 确定一切信息正确以后，就可以点击下一步确定来完成SSL的安装了。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/21.jpg" /></p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/22.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 默认安装结束后，SSL并没有启动我们需要自己给我们的站点SSL的加密通道，并且确定HTTPS使用的端口是443 。</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/23.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 第一次通过HTTPS进入站点的时候，会有一个对话框让我们确认是否同意当前证书，当然是同意啦~</p>
<p align="left">&nbsp;&nbsp;&nbsp; <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nj_1st_excellence/24.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 好了，这个时候我们看这个网站的时候所有信息在网上就是以加密的方式来传送的了，任何人都无法再轻易了解其中的内容了。</p>
<p>&nbsp;&nbsp;&nbsp; 加密过的SSL会比普通的没有加密的WEB浏览的时候慢一点，主要是因为加密的隧道额外还要占用一点CPU的资源，对于那些没有任何秘密可言的WEB站点没有需要用加密的SSL通道。只要对于那些重要的目录和站点才有这个必要性。<br />
</p>
<p><br />
&nbsp;</p>
<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=2051515</p>
<img src ="http://www.blogjava.net/superwei/aggbug/188437.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2008-03-25 11:19 <a href="http://www.blogjava.net/superwei/articles/188437.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Window.ShowModalDialog使用手册    转自 leavecd 的 Blog </title><link>http://www.blogjava.net/superwei/articles/180020.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Fri, 15 Feb 2008 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/180020.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/180020.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/180020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/180020.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/180020.html</trackback:ping><description><![CDATA[<span id="ArticleContent1_ArticleContent1_lblContent">基本介绍：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;showModalDialog()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(IE&nbsp;4+&nbsp;支持)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;showModelessDialog()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(IE&nbsp;5+&nbsp;支持)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.showModalDialog()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;方法用来创建一个显示HTML内容的模态对话框。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.showModelessDialog()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;方法用来创建一个显示HTML内容的非模态对话框。<br />
使用方法：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vReturnValue&nbsp;=&nbsp;window.showModalDialog(sURL&nbsp;[,&nbsp;vArguments]&nbsp;[,sFeatures])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vReturnValue&nbsp;=&nbsp;window.showModelessDialog(sURL&nbsp;[,&nbsp;vArguments]&nbsp;[,sFeatures])<br />
参数说明：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sURL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;&nbsp;&nbsp;必选参数，类型：字符串。用来指定对话框要显示的文档的URL。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vArguments&nbsp;&nbsp;&nbsp;--&nbsp;&nbsp;&nbsp;&nbsp;可选参数，类型：变体。用来向对话框传递参数。传递的参数类型不限，包括数组等。对话框通过window.dialogArguments来取得传递进来的参数。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sFeatures&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;&nbsp;&nbsp;&nbsp;可选参数，类型：字符串。用来描述对话框的外观等信息，可以使用以下的一个或几个，用分号&#8220;;&#8221;隔开。<br />
----------------<br />
1.&nbsp;&nbsp;&nbsp;dialogHeight:&nbsp;&nbsp;&nbsp;对话框高度，不小于100px<br />
2.&nbsp;&nbsp;&nbsp;dialogWidth:&nbsp;&nbsp;&nbsp;对话框宽度。<br />
3.&nbsp;&nbsp;&nbsp;dialogLeft:&nbsp;&nbsp;&nbsp;&nbsp;离屏幕左的距离。<br />
4.&nbsp;&nbsp;&nbsp;dialogTop:&nbsp;&nbsp;&nbsp;&nbsp;离屏幕上的距离。<br />
5.&nbsp;&nbsp;&nbsp;center:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;}&nbsp;：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是否居中，默认yes，但仍可以指定高度和宽度。<br />
6.&nbsp;&nbsp;&nbsp;help:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;}：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是否显示帮助按钮，默认yes。<br />
7.&nbsp;&nbsp;&nbsp;resizable:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;}&nbsp;[IE5+]：&nbsp;&nbsp;&nbsp;&nbsp;是否可被改变大小。默认no。<br />
8.&nbsp;&nbsp;&nbsp;status:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;}&nbsp;[IE5+]：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是否显示状态栏。默认为yes[&nbsp;Modeless]或no[Modal]。<br />
9.&nbsp;&nbsp;&nbsp;scroll:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;|&nbsp;on&nbsp;|&nbsp;off&nbsp;}：是否显示滚动条。默认为yes。<br />
<br />
下面几个属性是用在HTA中的，在一般的网页中一般不使用。<br />
10.&nbsp;&nbsp;&nbsp;dialogHide:{&nbsp;yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;|&nbsp;on&nbsp;|&nbsp;off&nbsp;}：在打印或者打印预览时对话框是否隐藏。默认为no。<br />
11.&nbsp;&nbsp;&nbsp;edge:{&nbsp;sunken&nbsp;|&nbsp;raised&nbsp;}：指明对话框的边框样式。默认为raised。<br />
12.&nbsp;&nbsp;&nbsp;unadorned:{&nbsp;yes&nbsp;|&nbsp;no&nbsp;|&nbsp;1&nbsp;|&nbsp;0&nbsp;|&nbsp;on&nbsp;|&nbsp;off&nbsp;}：默认为no。<br />
<br />
<br />
参数传递：<br />
1.&nbsp;&nbsp;&nbsp;要想对话框传递参数，是通过vArguments来进行传递的。类型不限制，对于字符串类型，最大为4096个字符。也可以传递对象，例如：<br />
-------------------------------<br />
parent.htm<br />
&lt;script&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;obj&nbsp;=&nbsp;new&nbsp;Object();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.name="51js";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.showModalDialog("modal.htm",obj,"dialogWidth=200px;dialogHeight=100px");<br />
&lt;/script&gt;<br />
modal.htm<br />
&lt;script&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;obj&nbsp;=&nbsp;window.dialogArguments<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert("您传递的参数为："&nbsp;+&nbsp;obj.name)<br />
&lt;/script&gt;<br />
-------------------------------<br />
2.&nbsp;&nbsp;&nbsp;可以通过window.returnValue向打开对话框的窗口返回信息，当然也可以是对象。例如：<br />
------------------------------<br />
parent.htm<br />
&lt;script&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;=window.showModalDialog("modal.htm",,"dialogWidth=200px;dialogHeight=100px");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert(str);<br />
&lt;/script&gt;<br />
modal.htm<br />
&lt;script&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.returnValue="http://www.51js.com";<br />
&lt;/script&gt;</span> <br />
<img src ="http://www.blogjava.net/superwei/aggbug/180020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2008-02-15 10:11 <a href="http://www.blogjava.net/superwei/articles/180020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转） 认识Web.config文件   </title><link>http://www.blogjava.net/superwei/articles/179777.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Wed, 13 Feb 2008 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/179777.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/179777.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/179777.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/179777.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/179777.html</trackback:ping><description><![CDATA[<div class="postText">
<p>Web.config 文件是一个XML文本文件，它用来储存 ASP.NET Web 应用程序的配置信息（如最常用的设置ASP.NET Web 应用程序的身份验证方式），它可以出现在应用程序的每一个目录中。当你通过.NET新建一个Web应用程序后，默认情况下会在根目录自动创建一个默认的Web.config文件，包括默认的配置设置，所有的子目录都继承它的配置设置。如果你想修改子目录的配置设置，你可以在该子目录下新建一个Web.config文件。它可以提供除从父目录继承的配置信息以外的配置信息，也可以重写或修改父目录中定义的设置。<br />
(一).Web.Config是以XML文件规范存储,配置文件分为以下格式<br />
1.配置节处理程序声明<br />
特点： 位于配置文件的顶部，包含在&lt;configSections&gt;标志中。<br />
2.特定应用程序配置<br />
特点: 位于&lt;appSetting&gt;中。 可以定义应用程序的全局常量设置等信息.<br />
3.配置节设置<br />
特点: 位于&lt;system.Web&gt;节中，控制Asp.net运行时的行为.<br />
4.配置节组<br />
特点: 用&lt;sectionGroup&gt;标记，可以自定义分组，可以放到&lt;configSections&gt;内部或其它&lt;sectionGroup&gt;标记的内部.<br />
(二).配置节的每一节<br />
1.&lt;configuration&gt;节<br />
根元素，其它节都是在它的内部.<br />
2.&lt;appSetting&gt;节<br />
此节用于定义应用程序设置项。对一些不确定设置，还可以让用户根据自己实际情况自己设置<br />
用法:<br />
I.<br />
&lt;appSettings&gt;<br />
&lt;add key="Conntction" value="server=192.168.85.66;userid=sa;password=;database=Info;"/&gt;<br />
&lt;appSettings&gt;<br />
定义了一个连接字符串常量，并且在实际应用时可以修改连接字符串，不用修改程式代码.<br />
II.&lt;appSettings&gt;<br />
&lt;add key="ErrPage" value="Error.aspx"/&gt;<br />
&lt;appSettings&gt;<br />
定义了一个错误重定向页面.<br />
3.&lt;compilation&gt;节<br />
格式:<br />
&lt;compilation <br />
defaultLanguage="c#"<br />
debug="true"<br />
/&gt;<br />
I.default language: 定义后台代码语言,可以选择C#和VB.net两种语言.<br />
IIdebug : 为true时，启动aspx调试； 为false不启动aspx调试，因而可以提高应用程序运行<br />
时的性能。 一般程序员在开发时设置为true,交给客户时设置为false.<br />
4.&lt;customErrors&gt;节<br />
格式:<br />
&lt;customErrors <br />
mode="RemoteOnly" <br />
defaultRedirect="error.aspx"<br />
&lt;error statusCode="440" redirect="err440page.aspx"/&gt;<br />
&lt;error statusCode="500" redirect="err500Page.aspx"/&gt;<br />
/&gt; <br />
I.mode : 具有On,Off,RemoteOnly 3种状态。On表示始终显示自定义的信息; Off表示始终显示详细的asp.net错误信息; RemoteOnly表示只对不在本地Web服务器上运行的用户显示自定义信息.<br />
II.defaultRedirect: 用于出现错误时重定向的URL地址. 是可选的<br />
III.statusCode: 指明错误状态码，表明一种特定的出错状态.<br />
IV. redirect:错误重定向的URL.<br />
5.&lt;globalization&gt;节<br />
格式:<br />
&lt;globalization <br />
requestEncoding="utf-8" <br />
responseEncoding="utf-8" <br />
fileEncoding="utf-8"<br />
/&gt; <br />
I.requestEncoding: 它用来检查每一个发来请求的编码. <br />
II.responseEncoding: 用于检查发回的响应内容编码.<br />
III.fileEncoding: 用于检查aspx,asax等文件解析的默认编码. <br />
6.&lt;sessionState&gt;节<br />
格式:<br />
&lt;sessionState <br />
mode="InProc"<br />
stateConnectionString="tcpip=127.0.0.1:42424"<br />
sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"<br />
cookieless="false" <br />
timeout="20" <br />
/&gt;<br />
I.mode: 分为off,Inproc,StateServer,SqlServer几种状态<br />
这里有详细介绍此属性: <a href="http://blog.csdn.net/chengking/archive/2005/10/27/518079.aspx">http://blog.csdn.net/chengking/archive/2005/10/27/518079.aspx</a> <br />
II. stateConnectionString :指定Asp.net应用程序存储远程会话状态的服务器名，默认为本机<br />
III.sqlConnectionString: 当用会话状态数据库时，在这里设置连接字符串<br />
IV. Cookieless: 设置为true时，表示不使用cookie会话状态来标识客户； 否则，相反.<br />
V. TimeOut: 用来定义会话状态存储的时间，超过期限，将自动终止会话.<br />
7.&lt;authentication&gt;节<br />
格式:<br />
&lt;authentication mode="Forms"&gt; <br />
&lt;forms name=".ASPXUSERDEMO" loginUrl="Login.aspx" protection="All" timeout="30"/&gt;<br />
&lt;/authentication&gt;<br />
&lt;authorization&gt;<br />
&lt;deny users="?"/&gt;<br />
&lt;/authorization&gt;<br />
I.Windows: 使用IIS验证方式<br />
II.Forms: 使用基于窗体的验证方式<br />
III.Passport: 采用Passport cookie验证模式<br />
IV.None: 不采用任何验证方式<br />
里面内嵌Forms节点的属性涵义:<br />
I.Name: 指定完成身份验证的Http cookie的名称.<br />
II.LoginUrl: 如果未通过验证或超时后重定向的页面URL，一般为登录页面，让用户重新登录<br />
III.Protection: 指定 cookie数据的保护方式. <br />
可设置为: All None Encryption Validation四种保护方式<br />
a. All表示加密数据，并进行有效性验证两种方式<br />
b. None表示不保护Cookie.<br />
c. Encryption表示对Cookie内容进行加密<br />
d. validation表示对Cookie内容进行有效性验证<br />
IV. TimeOut: 指定Cookie的失效时间. 超时后要重新登录.</p>
<p>在运行时对Web.config文件的修改不需要重启服务就可以生效（注：&lt;processModel&gt; 节例外）。当然Web.config文件是可以扩展的。你可以自定义新配置参数并编写配置节处理程序以对它们进行处理。</p>
<p>web.config配置文件（默认的配置设置）以下所有的代码都应该位于<br />
&lt;configuration&gt;<br />
&lt;system.web&gt;<br />
和<br />
&lt;/system.web&gt;<br />
&lt;/configuration&gt;<br />
之间，出于学习的目的下面的示例都省略了这段XML标记。</p>
<p>&nbsp;</p>
<p>1、&lt;authentication&gt; 节 <br />
作用：配置 ASP.NET 身份验证支持（为Windows、Forms、PassPort、None四种）。该元素只能在计算机、站点或应用程序级别声明。&lt; authentication&gt; 元素必需与&lt;authorization&gt; 节配合使用。</p>
<p>&nbsp;</p>
<p>示例：</p>
<p>以下示例为基于窗体（Forms）的身份验证配置站点，当没有登陆的用户访问需要身份验证的网页，网页自动跳转到登陆网页。<br />
&lt;authentication mode="Forms" &gt;<br />
&lt;forms loginUrl="logon.aspx" name=".FormsAuthCookie"/&gt;<br />
&lt;/authentication&gt;<br />
其中元素loginUrl表示登陆网页的名称，name表示Cookie名称。</p>
<p>2、&lt;authorization&gt; 节 <br />
作用：控制对 URL 资源的客户端访问（如允许匿名用户访问）。此元素可以在任何级别（计算机、站点、应用程序、子目录或页）上声明。必需与&lt;authentication&gt; 节配合使用。</p>
<p>&nbsp;</p>
<p>示例：以下示例禁止匿名用户的访问<br />
&lt;authorization&gt;<br />
&nbsp;&nbsp; &lt;deny users="?"/&gt;<br />
&lt;/authorization&gt;<br />
注：你可以使用user.identity.name来获取已经过验证的当前的用户名；可以使用web.Security.FormsAuthentication.RedirectFromLoginPage方法将已验证的用户重定向到用户刚才请求的页面.具体的</p>
<p>3、&lt;compilation&gt;节 <br />
作用：配置 ASP.NET 使用的所有编译设置。默认的debug属性为&#8220;True&#8221;.在程序编译完成交付使用之后应将其设为False（Web.config文件中有详细说明，此处省略示例）</p>
<p>&nbsp;</p>
<p>4、&lt;customErrors&gt; <br />
作用：为 ASP.NET 应用程序提供有关自定义错误信息的信息。它不适用于 XML Web services 中发生的错误。</p>
<p>示例：当发生错误时，将网页跳转到自定义的错误页面。<br />
&lt;customErrors defaultRedirect="ErrorPage.aspx" mode="RemoteOnly"&gt;<br />
&lt;/customErrors&gt;<br />
其中元素defaultRedirect表示自定义的错误网页的名称。mode元素表示：对不在本地 Web 服务器上运行的用户显示自定义(友好的)信息。</p>
<p>&nbsp;</p>
<p>5、&lt;httpRuntime&gt;节 <br />
作用：配置 ASP.NET HTTP 运行库设置。该节可以在计算机、站点、应用程序和子目录级别声明。</p>
<p>&nbsp;</p>
<p>示例：控制用户上传文件最大为4M，最长时间为60秒，最多请求数为100<br />
&lt;httpRuntime maxRequestLength="4096" executionTimeout="60" appRequestQueueLimit="100"/&gt;</p>
<p>6、 &lt;pages&gt; <br />
作用：标识特定于页的配置设置（如是否启用会话状态、视图状态，是否检测用户的输入等）。&lt;pages&gt;可以在计算机、站点、应用程序和子目录级别声明。</p>
<p>&nbsp;</p>
<p>示例：不检测用户在浏览器输入的内容中是否存在潜在的危险数据（注：该项默认是检测，如果你使用了不检测，一要对用户的输入进行编码或验证)，在从客户端回发页时将检查加密的视图状态，以验证视图状态是否已在客户端被篡改。(注：该项默认是不验证）<br />
&lt;pages buffer="true" enableViewStateMac="true" validateRequest="false"/&gt;</p>
<p>7、&lt;sessionState&gt; <br />
作用：为当前应用程序配置会话状态设置（如设置是否启用会话状态，会话状态保存位置）。</p>
<p>&nbsp;</p>
<p>示例：<br />
&lt;sessionState mode="InProc" cookieless="true" timeout="20"/&gt;<br />
&lt;/sessionState&gt;<br />
注： <br />
mode="InProc"表示：在本地储存会话状态（你也可以选择储存在远程服务器或SAL服务器中或不启用会话状态）<br />
cookieless="true"表示：如果用户浏览器不支持Cookie时启用会话状态(默认为False）<br />
timeout="20"表示：会话可以处于空闲状态的分钟数</p>
<p>8、&lt;trace&gt; <br />
作用：配置 ASP.NET 跟踪服务，主要用来程序测试判断哪里出错。</p>
<p>&nbsp;</p>
<p>示例：以下为Web.config中的默认配置：<br />
&lt;trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" /&gt;<br />
注： <br />
enabled="false"表示不启用跟踪；<br />
requestLimit="10"表示指定在服务器上存储的跟踪请求的数目<br />
pageOutput="false"表示只能通过跟踪实用工具访问跟踪输出；<br />
traceMode="SortByTime"表示以处理跟踪的顺序来显示跟踪信息<br />
localOnly="true" 表示跟踪查看器 (trace.axd) 只用于宿主 Web 服务器</p>
<p>自定义Web.config文件配置 </p>
<p>自定义Web.config文件配置节过程分为两步。 <br />
1.在在配置文件顶部 &lt;configSections&gt; 和 &lt;/configSections&gt;标记之间声明配置节的名称和处理该节中配置数据的 .NET Framework 类的名称。<br />
2.是在 &lt;configSections&gt; 区域之后为声明的节做实际的配置设置。</p>
<p>&nbsp;</p>
<p>示例：创建一个节存储数据库连接字符串<br />
&lt;configuration&gt;<br />
　 &lt;configSections&gt;<br />
　 &lt;section name="appSettings" type="System.Configuration.NameValueFileSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/&gt;<br />
&lt;/configSections&gt;<br />
　 &lt;appSettings&gt;<br />
　　 &lt;add key="scon" value="server=a;database=northwind;uid=sa;pwd=123"/&gt;<br />
　 &lt;/appSettings&gt;<br />
　 &lt;system.web&gt;<br />
　　 ......<br />
　 &lt;/system.web&gt;<br />
&lt;/configuration&gt;</p>
<p>访问Web.config文件 你可以通过使用ConfigurationSettings.AppSettings 静态字符串集合来访问 Web.config 文件示例：获取上面例子中建立的连接字符串。例如：<br />
protected static string Isdebug = ConfigurationSettings.AppSettings["debug"]</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>二、web.config中的session配置详解<br />
打开某个应用程序的配置文件Web.config后，我们会发现以下这段：<br />
&lt; sessionState<br />
　　mode="InProc"<br />
　　stateConnectionString="tcpip=127.0.0.1:42424"<br />
　　sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"<br />
　　cookieless="false"<br />
　　timeout="20" <br />
/&gt; <br />
　　这一段就是配置应用程序是如何存储Session信息的了。我们以下的各种操作主要是针对这一段配置展开。让我们先看看这一段配置中所包含的内容的意思。sessionState节点的语法是这样的：<br />
&lt; sessionState mode="Off|InProc|StateServer|SQLServer"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cookieless="true|false"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout="number of minutes"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stateConnectionString="tcpip=server:port"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlConnectionString="sql connection string"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stateNetworkTimeout="number of seconds"<br />
/&gt;</p>
<p>必须有的属性是 属性 选项 描述<br />
mode 设置将Session信息存储到哪里 <br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Off 设置为不使用Session功能，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InProc 设置为将Session存储在进程内，就是ASP中的存储方式，这是默认值，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StateServer 设置为将Session存储在独立的状态服务中，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLServer 设置将Session存储在SQL Server中。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>可选的属性是： 属性 选项 描述 <br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cookieless 设置客户端的Session信息存储到哪里，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ture 使用Cookieless模式，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; false 使用Cookie模式，这是默认值，<br />
&#216;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout 设置经过多少分钟后服务器自动放弃Session信息，默认为20分钟。<br />
stateConnectionString 设置将Session信息存储在状态服务中时使用的服务器名称和端口号，例如："tcpip=127.0.0.1:42424&#8221;。当mode的值是StateServer是，这个属性是必需的。 <br />
sqlConnectionString 设置与SQL Server连接时的连接字符串。例如"data source= localhost;Integrated Security=SSPI;Initial Catalog=northwind"。当mode的值是 SQLServer时，这个属性是必需的。 <br />
stateNetworkTimeout 设置当使用StateServer模式存储Session状态时，经过多少秒空闲后，断开Web服务器与存储状态信息的服务器的TCP/IP连接的。默认值是10秒钟。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>ASP.NET中客户端Session状态的存储 <br />
　　在我们上面的Session模型简介中，大家可以发现Session状态应该存储在两个地方，分别是客户端和服务器端。客户端只负责保存相应网站的SessionID，而其他的Session信息则保存在服务器端。在ASP中，客户端的SessionID实际是以Cookie的形式存储的。如果用户在浏览器的设置中选择了禁用Cookie，那末他也就无法享受Session的便利之处了，甚至造成不能访问某些网站。为了解决以上问题，在 ASP.NET中客户端的Session信息存储方式分为：Cookie和Cookieless两种。<br />
　　ASP.NET中，默认状态下，在客户端还是使用Cookie存储Session信息的。如果我们想在客户端使用Cookieless的方式存储Session信息的方法如下：<br />
　　找到当前Web应用程序的根目录，打开Web.Config文件，找到如下段落：<br />
&lt; sessionState<br />
　　mode="InProc"<br />
　　stateConnectionString="tcpip=127.0.0.1:42424"<br />
　　sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"<br />
　　cookieless="false"<br />
　　timeout="20" <br />
/&gt;<br />
　　这段话中的cookieless="false"改为：cookieless="true"，这样，客户端的Session信息就不再使用 Cookie存储了，而是将其通过URL存储。关闭当前的IE，打开一个新IE，重新访问刚才的Web应用程序，就会看到类似下面的样子：<br />
其中，<a href="http://localhost/MyTestApplication/(ulqsek45heu3ic2a5zgdl245">http://localhost/MyTestApplication/(ulqsek45heu3ic2a5zgdl245</a>) /default.aspx中黑体标出的就是客户端的Session ID。注意，这段信息是由IIS自动加上的，不会影响以前正常的连接。</p>
<p>&nbsp;</p>
<p>ASP.NET中服务器端Session状态的存储 准备工作：<br />
　　为了您能更好的体验到实验现象，您可以建立一个叫做SessionState.aspx的页面，然后把以下这些代码添加到&lt; body&gt;&lt; /body&gt;中。<br />
&lt; scriptrunat="server"&gt;<br />
Sub Session_Add(sender As Object, e As EventArgs)<br />
　 Session("MySession") = text1.Value<br />
　 span1.InnerHtml = "Session data updated! &lt; P&gt;Your session contains: &lt; font color=red&gt;" &amp; Session("MySession"). ToString() &amp; "&lt; /font&gt;"<br />
End Sub<br />
Sub CheckSession(sender As Object, eAs EventArgs)<br />
　 If (Session("MySession")Is Nothing) Then<br />
　　　 span1.InnerHtml = "NOTHING, SESSION DATA LOST!"<br />
　 Else<br />
　　　 span1.InnerHtml = "Your session contains: &lt; font color= red&gt;" &amp; Session("MySession").ToString() &amp; "&lt;&nbsp;&nbsp; /font&gt;"<br />
End If<br />
End Sub<br />
&lt; /script&gt;<br />
&lt; formrunat="server"id="Form2"&gt;<br />
　 &lt; inputid="text1"type="text"runat="server"name="text1"&gt;<br />
　 &lt; inputtype="submit"runat="server"OnServerClick="Session_Add"<br />
　　　　　 value="Add to Session State " id="Submit1"name="Submit1"&gt;<br />
　 &lt; inputtype="submit"runat="server"OnServerClick="CheckSession"<br />
　　　　　 value=" View Session State " id="Submit2"name="Submit2"&gt;<br />
&lt; /form&gt;<br />
&lt; hrsize="1"&gt;<br />
&lt; fontsize="6"&gt;&lt; spanid="span1"runat="server" /&gt;&lt; /font&gt; <br />
　　这个SessionState.aspx的页面可以用来测试在当前的服务器上是否丢失了Session信息。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>将服务器Session信息存储在进程中 <br />
　　让我们来回到Web.config文件的刚才那段段落中：<br />
&lt; sessionState<br />
　　mode="InProc"<br />
　　stateConnectionString="tcpip=127.0.0.1:42424"<br />
　　sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"<br />
　　cookieless="false"<br />
　　timeout="20" <br />
/&gt;<br />
　　当mode的值是InProc时，说明服务器正在使用这种模式。<br />
　　这种方式和以前ASP中的模式一样，就是服务器将Session信息存储在IIS进程中。当IIS关闭、重起后，这些信息都会丢失。但是这种模式也有自己最大好处，就是性能最高。应为所有的Session信息都存储在了IIS的进程中，所以IIS能够很快的访问到这些信息，这种模式的性能比进程外存储Session信息或是在SQL Server中存储Session信息都要快上很多。这种模式也是ASP.NET的默认方式。<br />
　　好了，现在让我们做个试验。打开刚才的SessionState.aspx页面，随便输入一些字符，使其存储在Session中。然后，让我们让IIS重起。注意，并不是使当前的站点停止再开始，而是在IIS中本机的机器名的节点上点击鼠标右键，选择重新启动IIS。(想当初使用NT4时，重新启动IIS必须要重新启动计算机才行，微软真是@#$%^&amp;)返回到SessionState.aspx页面中，检查刚才的Session信息，发现信息已经丢失了。</p>
<p>&nbsp;</p>
<p>将服务器Session信息存储在进程外 <br />
　　首先，让我们来打开管理工具-&gt;服务，找到名为：ASP.NET State Service的服务，启动它。实际上，这个服务就是启动一个要保存Session信息的进程。启动这个服务后，你可以从Windows任务管理器-&gt;进程中看到一个名为 aspnet_state.exe的进程，这个就是我们保存Session信息的进程。<br />
　　然后，回到Web.config文件中上述的段落中，将mode的值改为StateServer。保存文件后的重新打开一个IE，打开 SessionState.aspx页面，保存一些信息到Session中。这时，让我们重起IIS，再回到SessionState.aspx页面中查看刚才的Session信息，发现没有丢失。<br />
　　实际上，这种将Session信息存储在进程外的方式不光指可以将信息存储在本机的进程外，还可以将Session信息存储在其他的服务器的进程中。这时，不光需要将mode的值改为StateServer，还需要在stateConnectionString中配置相应的参数。例如你的计算你是192.168.0.1，你想把Session存储在IP为192.168.0.2的计算机的进程中，就需要设置成这样： stateConnectionString="tcpip=192.168.0.2:42424"。当然，不要忘记在192.168.0.2的计算机中装上.NET Framework，并且启动ASP.NET State Services服务。</p>
<p>&nbsp;</p>
<p>将服务器Session信息存储在SQL Server中 <br />
　　首先，还是让我们来做一些准备工作。启动SQL Server和SQL Server代理服务。在SQL Server中执行一个叫做 InstallSqlState.sql的脚本文件。这个脚本文件将在SQL Server中创建一个用来专门存储Session信息的数据库，及一个维护Session信息数据库的SQL Server代理作业。我们可以在以下路径中找到那个文件：<br />
[system drive]\winnt\Microsoft.NET\Framework\[version]\ <br />
　　然后打开查询分析器，连接到SQL Server服务器，打开刚才的那个文件并且执行。稍等片刻，数据库及作业就建立好了。这时，你可以打开企业管理器，看到新增了一个叫ASPState的数据库。但是这个数据库中只是些存储过程，没有用户表。实际上Session信息是存储在了tempdb 数据库的ASPStateTempSessions表中的，另外一个ASPStateTempApplications表存储了ASP中 Application对象信息。这两个表也是刚才的那个脚本建立的。另外查看管理-&gt;SQL Server代理-&gt;作业，发现也多了一个叫做ASPState_Job_DeleteExpiredSessions的作业，这个作业实际上就是每分钟去ASPStateTempSessions 表中删除过期的Session信息的。<br />
　　接着，我们返回到Web.config文件，修改mode的值改为SQLServer。注意，还要同时修改sqlConnectionString的值，格式为：<br />
sqlConnectionString="data source=localhost; Integrated Security=SSPI;"<br />
　　其中data source是指SQL Server服务器的IP地址，如果SQL Server与IIS是一台机子，写127.0.0.1 就行了。Integrated Security=SSPI的意思是使用Windows集成身份验证，这样，访问数据库将以ASP.NET的身份进行，通过如此配置，能够获得比使用userid=sa;password=口令的SQL Server验证方式更好的安全性。当然，如果SQL Server运行于另一台计算机上，你可能会需要通过Active Directory域的方式来维护两边验证的一致性。<br />
　　同样，让我们做个试验。向SessionState.aspx中添加Session信息，这时发现Session信息已经存在 SQL Server中了，即使你重起计算机，刚才的Session信息也不会丢失。现在，你已经完全看见了Session信息到底是什么样子的了，而且又是存储在SQL Server中的，能干什么就看你的发挥了。</p>
<p>总结 <br />
　　通过这篇文章，你可以看到在Session的管理和维护上，ASP.NET比ASP有了很大的进步，我们可以更加随意的挑选适合的方法了。对于企业级的应用来说，这无疑对于服务器的同步、服务器的稳定性、可靠性都是有利的。相信在强大的微软支持下，新一代的电子商务平台将会搭建的更好！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同时，大家也会发现，在这个整个技术中包括了操作系统、Web服务及数据库多种技术的整合。我相信，也许Windows没有Unix稳定， IIS没有Apache稳定，SQL Server也没有Oracle强大，但是，谁可以将他们如此完美的联动到一起呢？所以说，虽然微软每一方面都不是太强，但是如果把微软的东西都整合到一起，谁敢说他不强大呢？微软就是微软！</p>
<p>&nbsp;</p>
<p>三、Asp.net 关于form认证的一般设置<br />
asp.net 关于form认证的一般设置：<br />
1: 在web.config中，加入form认证； <br />
&nbsp;&nbsp; &lt;authentication mode="Forms"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;forms name="auth" loginUrl="index.aspx" timeout="30"&gt;&lt;/forms&gt;<br />
&lt;/authentication&gt;<br />
&lt;authorization&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;deny users="?" /&gt;<br />
&lt;/authorization&gt;<br />
2: 如果有注册页面时还应该允许匿名用户调用注册页面进行注册;<br />
以下代码应该在&lt;configuration&gt;&lt;system.web&gt;之间,而不应该包含到&lt;system.web&gt;..&lt;/system.web&gt;之间;<br />
----------------表示允许 匿名用户对 userReg.aspx页面进行访问.<br />
&lt;location path="userReg.aspx"&gt;<br />
&nbsp; &lt;system.web&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;authorization&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;allow users="?" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/authorization&gt;<br />
&nbsp; &lt;/system.web&gt;<br />
&lt;/location&gt;<br />
3 在登录成功后要创建身份验证票, 表明已经通过认证的合法用户;<br />
if(登陆成功)</p>
<p>System.Web.Security.FormsAuthentication.SetAuthCookie(用户名称, false);<br />
　　四、访问Web.config文件<br />
　　你可以通过使用ConfigurationSettings.AppSettings 静态字符串集合来访问 Web.config 文件示例：获取上面例子中建立的连接字符串。例如：</p>
<p>protected static string Isdebug = ConfigurationSettings.AppSettings["scon"] <br />
该文章转自Doorle's Blog-多乐博客：<a href="http://www.doorle.cn/blog/article.asp?id=923">http://www.doorle.cn/blog/article.asp?id=923</a>&nbsp;</p>
</div>
<img src ="http://www.blogjava.net/superwei/aggbug/179777.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2008-02-13 14:47 <a href="http://www.blogjava.net/superwei/articles/179777.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB.NET2003 RecordSet转成DataSet代码（原创）</title><link>http://www.blogjava.net/superwei/articles/148759.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Thu, 27 Sep 2007 08:19:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/148759.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/148759.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/148759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/148759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/148759.html</trackback:ping><description><![CDATA[<p>Module RecordSetToDataSet<br />
&nbsp;&nbsp;&nbsp; Public Function collectionChange(ByVal _ODyn As OracleInProcServer.OraDynaset) As DataSet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim countR As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim countC As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim i As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim j As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim aoDataSet As DataSet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataSet = New DataSet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim aoTable As DataTable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoTable = New DataTable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; countR = _ODyn.RecordCount<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Get ColumnsName From 0 to N<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' eg: ODyn.FieldName(0) = "MSGID"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim str1 As String = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim str2 As String = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'get collumName<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For i = 0 To 1000<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str1 = _ODyn.FieldName(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If str1.Equals(String.Empty) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If str2.Length &gt; 0 Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str2 = Mid(str2, 1, str2.Length - 1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit For<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str2 = str2 + str1 + ","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoTable.Columns.Add(str1, Type.GetType("System.String"))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; countC = countC + 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'aoTable.Rows.Add <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For i = 0 To countR - 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim newR As DataRow<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newR = aoTable.NewRow<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For j = 0 To countC - 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If TypeOf _ODyn.Item(j).Value Is System.DBNull Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str1 = "&lt;NULL&gt;"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str1 = CStr(_ODyn.Item(j).Value)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If str1 = "" Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str1 = "null"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newR(j) = str1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next j<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoTable.Rows.Add(newR)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _ODyn.MoveNext()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataSet.Tables.Add(aoTable)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return aoDataSet<br />
&nbsp;&nbsp;&nbsp; End Function<br />
End Module</p>
<img src ="http://www.blogjava.net/superwei/aggbug/148759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-09-27 16:19 <a href="http://www.blogjava.net/superwei/articles/148759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB.NET2003 用OO4O方式连接Oracle（原创）</title><link>http://www.blogjava.net/superwei/articles/148757.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Thu, 27 Sep 2007 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/148757.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/148757.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/148757.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/148757.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/148757.html</trackback:ping><description><![CDATA[<p>#Region "&nbsp;&nbsp; ' OO4O Connect"<br />
&nbsp;&nbsp;&nbsp; Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim strSQL1 As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim strSQL2 As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim n As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim j As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim countC As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim countR As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim obj As Object<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim iora As OracleInProcServer.OraField<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim aoDataSet As DataSet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataSet = New DataSet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim aoTable As DataTable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataSet.Tables.Add("aoTable")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim ODyn As OraDynaset</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strSQL1 = "UPDATE T_MSG SET DELFLG = 0 WHERE MSGDISP = 1"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strSQL2 = "SELECT * FROM superwei "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim blnConnect As Boolean = OO4O_Connection()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If blnConnect = False Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit Sub<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataBase.BeginTrans()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Begin Transaction<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'n = aoDataBase.ExecuteSQL(strSQL1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' normal SQL run<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ODyn = aoDataBase.CreateDynaset(strSQL2, ORADYN_READONLY)&nbsp;&nbsp; ' return OraDynaset<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If ODyn.BOF Or ODyn.EOF Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgBox("Empty")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit Sub<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;&nbsp;&nbsp;&nbsp;&nbsp; ODyn.MoveFirst()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataSet = collectionChange(ODyn)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'DataGrid1.DataSource = aoDataSet.Tables(0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Call disData(DataGrid1, aoDataSet)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataBase.Rollback()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Catch ex As Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgBox(Me.Text &amp; ":" &amp; ex.Message)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextBox1.Text = Me.Text &amp; ":" &amp; ex.Message<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataBase.Rollback()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Try<br />
&nbsp;&nbsp;&nbsp; End Sub</p>
<p>&nbsp;&nbsp;&nbsp; Function disData(ByVal Dgd As DataGrid, ByVal aoDataSet As DataSet)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dgd = Me.DataGrid1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dgd.DataSource = aoDataSet.Tables(0)<br />
&nbsp;&nbsp;&nbsp; End Function<br />
#End Region<br />
<br />
</p>
<p>Module OO4O_Connect<br />
&nbsp;&nbsp;&nbsp; Public aoDataBase As OracleInProcServer.OraDatabase<br />
&nbsp;&nbsp;&nbsp; Public Overloads Function OO4O_Connection() As Boolean<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim oOraSession As OracleInProcServer.OraSession<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim oOraDataBase As Object<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim sConnectString As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim asHostName As String = "ips"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sConnectString = "ips/ips"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oOraSession = CreateObject("OracleInProcServer.XOraSession")</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aoDataBase = oOraSession.OpenDatabase(asHostName, sConnectString, 0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OO4O_Connection = True</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Catch ex As Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgBox(ex.Message &amp; " " &amp; ex.ToString)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OO4O_Connection = False<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Try<br />
&nbsp;&nbsp;&nbsp; End Function</p>
<p>&nbsp;&nbsp;&nbsp; Public Overloads Function OO4O_Connection(ByVal _aoDataBase As OracleInProcServer.OraDatabase) As OracleInProcServer.OraDatabase<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim oOraSession As OracleInProcServer.OraSession<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim oOraDataBase As Object<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim sConnectString As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim asHostName As String = "ips"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sConnectString = "cho_objown/cho_objown"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oOraSession = CreateObject("OracleInProcServer.XOraSession")</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _aoDataBase = oOraSession.OpenDatabase(asHostName, sConnectString, 0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return _aoDataBase</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Catch ex As Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgBox(ex.Message &amp; " " &amp; ex.ToString)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'OO4O_Connection = False<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Try<br />
&nbsp;&nbsp;&nbsp; End Function</p>
<p>End Module<br />
</p>
<p><br />
<br />
<br />
</p>
 <img src ="http://www.blogjava.net/superwei/aggbug/148757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-09-27 16:15 <a href="http://www.blogjava.net/superwei/articles/148757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB.NET2003 DataSet导出到CSV代码（原创）</title><link>http://www.blogjava.net/superwei/articles/148756.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Thu, 27 Sep 2007 08:11:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/148756.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/148756.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/148756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/148756.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/148756.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; Public Function OutPutCSV(ByVal FileName As String, ByVal DataTable As DataSet, Optional ByVal TableIndex As Integer = 0) As Boolean<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim fw As StreamWriter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim myRow As DataRow<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim myCol As DataColumn<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw = New StreamWriter(FileName, True)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each myCol In DataTable.Tables(TableIndex).Columns<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If myCol Is DataTable.Tables(TableIndex).Columns(DataTable.Tables(TableIndex).Columns.Count - 1) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.WriteLine(myCol.Caption)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.Write(myCol.Caption)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.Write(",")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next myCol<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each myRow In DataTable.Tables(TableIndex).Rows<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each myCol In DataTable.Tables(TableIndex).Columns<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If myCol Is DataTable.Tables(TableIndex).Columns(DataTable.Tables(TableIndex).Columns.Count - 1) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.WriteLine(myRow(myCol))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.Write(myRow(myCol))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.Write(",")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next myCol<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next myRow<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return True<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Catch e As Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show(e.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return False<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Finally<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fw.Close()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Try<br />
&nbsp;&nbsp;&nbsp; End Function
<img src ="http://www.blogjava.net/superwei/aggbug/148756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-09-27 16:11 <a href="http://www.blogjava.net/superwei/articles/148756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB.NET 2003 调用Oracle的存储过程并返回游标值（原创）</title><link>http://www.blogjava.net/superwei/articles/148750.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Thu, 27 Sep 2007 08:05:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/148750.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/148750.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/148750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/148750.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/148750.html</trackback:ping><description><![CDATA[这些都是去年的代码，怕遗忘，也为了方便查找，所以放到这里保存！<br />
建立工程就不说了<br />
1、新建一个Module，添加代码如下：<br />
<p>&nbsp;&nbsp;&nbsp; Public Function runProduce(ByVal _arrayPro() As OracleParameter, ByVal _strProName As String, ByVal _strCon As OracleClient.OracleConnection)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim total As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim ocmd As New OracleCommand(_strProName, _strCon)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim oda As New OracleDataAdapter(ocmd)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim dsSales As New DataSet</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ocmd.CommandType = CommandType.StoredProcedure</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim errcdPara As New OracleParameter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; With errcdPara<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ParameterName = "P_ERRCD"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .OracleType = OracleType.Char<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Size = 88<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Direction = ParameterDirection.InputOutput<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Value = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End With<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim errMsgPara As New OracleParameter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; With errMsgPara<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ParameterName = "P_ERRMSG"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .OracleType = OracleType.VarChar<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Size = 88<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Direction = ParameterDirection.InputOutput<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Value = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End With</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ocmd.Parameters.Add(errcdPara)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ocmd.Parameters.Add(errMsgPara)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For total = UBound(_arrayPro) To 0 Step -1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ocmd.Parameters.Add(_arrayPro(total))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oda.Fill(dsSales)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Catch exp As Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'MessageBox.Show(exp.Message, MessageBoxButtons.OK, _<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '&nbsp;&nbsp;&nbsp; MessageBoxIcon.Error)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgBox(exp.Message)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit Function<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _strCon.Close()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return dsSales<br />
&nbsp;&nbsp;&nbsp; End Function</p>
参数：_arrayPro() 存储过程的参数；_strProName 存储过程名；_strCon Oracle的连接对象<br />
<br />
2、执行代码<br />
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim strConn As oracConnection = oracConn()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim arrayPro(1) As OracleParameter</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim sorPara As New OracleParameter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; With sorPara<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ParameterName = "SOR_SHIWAKE"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .OracleType = OracleType.Cursor<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Direction = ParameterDirection.Output<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Value = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End With<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arrayPro(0) = sorPara</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim salePara As New OracleParameter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; With salePara<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ParameterName = "P_SALEYMD"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .OracleType = OracleType.VarChar<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Direction = ParameterDirection.Input<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Value = strInput<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End With<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arrayPro(1) = salePara</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If strConn.isConn = True Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim dsDataSet&nbsp;As DataSet&nbsp;= runProduce(arrayPro, @produceName, conn)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br />
<br />
.NET调用存储过程有两种方法，以上是其中一种比较简便的方法（自认为），<br />
另一种也简单，就是做成一条SQL语句来执行，并返回结果。但是手头没有代码，也就写到这里了</p>
<br />
<br />
<img src ="http://www.blogjava.net/superwei/aggbug/148750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-09-27 16:05 <a href="http://www.blogjava.net/superwei/articles/148750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为 Microsoft Visual Studio .NET 设计器创建可设计的组件</title><link>http://www.blogjava.net/superwei/articles/124685.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Sat, 16 Jun 2007 12:32:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/124685.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/124685.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/124685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/124685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/124685.html</trackback:ping><description><![CDATA[<h1>转自:http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic8</h1>
Shawn Burke<br>Microsoft Corporation <br>2000年7月
<p>&#160;</p>
<p><strong>摘要：</strong>Microsoft .NET 组件是以管理代码编写并在通用语言运行时上构建的。本文论述了它是如何向开发人员提供了一个全新的混合开发环境，即象 Microsoft Visual Basic 一样容易，而同时又提供了强大的低级编程能力，与 ATL 或 MFC 更加相关。</p>
<h4>目录</h4>
<ul>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic1"><u><font color=#800080>简介</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic2"><u><font color=#800080>组件是什么</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic3"><u><font color=#800080>定制元数据</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic4"><u><font color=#800080>属性浏览器接口</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic5"><font color=#800080><u>从外部引入：代码持续性 </u></font></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic6"><u><font color=#800080>组件设计器</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic7"><u><font color=#800080>访问设计器服务和基础结构</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic8"><u><font color=#800080>为组件授予许可证</font></u></a>
    <li><a href="http://www.microsoft.com/china/msdn/archives/library/techart/pdc_vsdescmp.ASP#pdc_vsdescmp_topic9"><u><font color=#800080>结论</font></u></a></li>
</ul>
<h2><a name=pdc_vsdescmp_topic1></a>简介</h2>
<p>微软即将发布的 Visual Studio .NET 将使程序开发人员获得一个集成开发环境，它不但为开发传统的 C/C++ 应用程序，而且也为令人振奋的 Microsoft .NET 组件提供了丰富的工具。这些以管理代码编写、在通用语言运行时构建的组件向开发人员提供了一个全新的混合开发环境，即象 Microsoft Visual Basic 一样容易，而同时又提供了强大的低级编程能力，与 ATL 或 MFC 更加相关。随着以生产效率为中心的管理环境的到来，它可与传统 COM 组件很好地协同工作。开发人员可以将更多时间花在构建大型组件上，而不用再为内存泄漏、安全和头文件担心。</p>
<p>除了提供 Microsoft .NET Framework 组件的开发外，Visual Studio .NET (VS .NET) 还拥有很多工具，可以让组件利用 VS .NET 中设计器架构的优势来设计出在外观和性能上与 VS .NET 所附带组件相近的产品。在开发管理组件时，在 VS.NET 设计器中获得的所有特性都使用组件本身的 .NET Framework，从而获得设计时与运行时组件之间的紧密集成。 </p>
<h2><a name=pdc_vsdescmp_topic2></a>组件是什么</h2>
<p>很显然，Microsoft .NET Framework 组件很容易编写。让它们与 Visual Studio .NET 设计器一同工作的唯一要求是它们实现 System.ComponentModel.IComponent，即通常表明继承于 <strong>IComponent</strong> 的默认应用。<strong>IComponent</strong> 使组件可跟踪设计时的信息（如它的容器组件或名称）或访问设计器提供的服务。</p>
<p>让我们编写一个简单的 .NET 组件，它的形式如下：</p>
<pre><code>using System;
using System.ComponentModel;
public class BoolTracker : Component {
private bool state;
private EventHandler handler;
private static object EventValueChanged = new object();
public BoolTracker() {
}
public bool Value {
get {
return state;
}
set {
if (this.state != value) {
this.state = value;
OnValueChanged(new EventArgs());
}
}
}
public void AddOnValueChanged(EventHandler h) {
handler  = (EventHandler)Delegate.Combine(handler, h);
}
protected virtual void OnValueChanged(EventArgs e) {
if (handler != null) {
handler(this, e);
}
}
public void RemoveOnValueChanged(EventHandler h) {
handler = (EventHandler)Delegate.Remove(handler, h);
}
}
</code></pre>
<p>显然，这个组件不完成什么功能, 但会将它置入 Visual Studio .NET Win 窗体设计器或组件设计器中，即可从属性浏览器中看见它有名称，也有一个称为 &#8220;Value&#8221; 的属性，使用下拉箭头可以将值设置为 True 或 False，当值在 True 和 False 之间切换时，可以触发事件 <strong>OnValueChanged</strong>。</p>
<p>对于设计器来说，组件只是我们要说明的一半，最重要的部分是属性，它组成了<em>元数据，</em>元数据是关于类、属性、事件等的信息。让我们以 <strong>Value</strong> 属性为例。仅作为属性，就已经有相关的元数据了，例如类型（布尔）、行为（读/写）或名称（&#8220;Value&#8221;）。使用&#8220;反射&#8221;对基本元数据进行检索，即通用语言运行时允许用户在运行时检查对象的类型、基本类型、属性、方法、构造器、字段和访问级别。所有这些信息都被认为是元数据。</p>
<h2><a name=pdc_vsdescmp_topic3></a>定制元数据</h2>
<p>定制元数据包括可添加到类或类成员的任意信息段（字段、属性或方法），实际上是类型本身被特定客户所识别。对于Visual Studio .NET 设计器来说，定制元数据构成了所有可扩展性的基础。VS .NET 设计器理解的所有元数据属性都基于一个名为 System.ComponentModel.MemberAttribute 的类。它提供一个基本类，因此开发器所关心的属性可以通过它们的类型快速标识。</p>
<p>通过一个典型实例可以更容易理解这一概念。比如我们不希望 <strong>Value</strong> 属性在属性浏览器中显示。我们可以添加一个元数据属性 System.ComponentModel.BrowsableAttribute 来控制一个属性是否可被浏览。</p>
<pre><code> [Browsable(false)]
public bool Value {
&nbsp;&nbsp;&nbsp;get {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return state;
}
&nbsp;&nbsp;&nbsp;set {
&nbsp;&nbsp;&nbsp;if (this.state != value) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.state = value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnValueChanged(new EventArgs());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;}
}
</code></pre>
<p>在指定属性时，可以将&#8220;BrowsableAttribute&#8221;缩略为&#8220;Browsable&#8221;。由 C# 编译器为我们添加&#8220;Attribute&#8221;一词。唯一的限制是如果指定了属性值，它必须与构造器的属性类型相符，且该数值必须是常量。在本例中，BrowsableAttribute 有一个单一的布尔型参数&#8220;Browsable&#8221;的构造器，编译器把这个元数据属性绑定到该构造器并创建一个属性类的实例。如果属性类浏览器获得了这个对象，它将枚举出该对象的属性并忽略&#8220;browsable&#8221;属性，因为它以此属性为标签。因此看起来该对象没有属性。BrowsableAttribute 也可应用于事件。</p>
<p>Microsoft .NET Framework 拥有丰富的属性集来控制设计器如何使用组件。这里是其中一些有用属性的列表，使您在以后的阅读中更能理解其含义：</p>
<table cols=2 cellPadding=5 rules=rows border=1 frame=below>
    <tbody>
        <tr vAlign=top>
            <td class=label width="31%"><font class=90v><strong>属性名</strong></font></td>
            <td class=label width="69%"><font class=90v><strong>说明</strong></font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>BrowsableAttribute</font></td>
            <td width="69%"><font class=90v>控制属性或事件是否显示在属性浏览器中。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>BindableAttribute</font></td>
            <td width="69%"><font class=90v>确定属性是否适合数据绑定器进行绑定。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>CategoryAttribute</font></td>
            <td width="69%"><font class=90v>指定属性在属性浏览器中应分组的类别(&#8220;Appearance&#8221;, &#8220;Layout&#8221;, &#8220;Behavior&#8221;,&#8220; Misc&#8221;等等）。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>DefaultEventAttribute/ DefaultPropertyAttribute</font></td>
            <td width="69%"><font class=90v>指定对象的默认事件或属性。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>HelpAttribute</font></td>
            <td width="69%"><font class=90v>指定属性或事件的帮助文件和主题。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>LicenseProviderAttribute</font></td>
            <td width="69%"><font class=90v>指向为组件提供许可证信息的 LicenseProvider。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>MergablePropertyAttribute</font></td>
            <td width="69%"><font class=90v>在属性浏览器中当多个组件被浏览和选中时，允许或阻止包含某属性。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>PersistableAttribute</font></td>
            <td width="69%"><font class=90v>确定在 Win Forms Designer 或 Component Designer 等可视设计器中生成代码时，属性值是否应与代码保持一致。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>PersistContentsAttribute </font></td>
            <td width="69%"><font class=90v>确定代码生成是否应回归到对象的非数值类型属性以及是否保持代码与属性值一致。<strong>ICollection</strong> 属性类型是这一应用的典型示例。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>ShowInToolboxAttribute</font></td>
            <td width="69%"><font class=90v>确定是否允许在工具框中使用这一组件。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="31%"><font class=90v>ToolBoxItemAttriubte</font></td>
            <td width="69%"><font class=90v>指定从工具框中创建类时应使用的 ToolboxItem 类型。</font></td>
        </tr>
    </tbody>
</table>
<br>
<h2><a name=pdc_vsdescmp_topic4></a>属性浏览器接口</h2>
<p>你或许注意到属性浏览器已在上一部分中多次提到。这是因为在设计器中组件参与的大量活动发生在属性浏览器中。设计器组织并显示组件，但是主要是由对象浏览器决定允许可以修改它们。在以前的版本中，浏览器显示自带的 COM 对象，这些对象拥有要显示的数据类型的已定义子集：数字、字符串、字体、颜色、图像、布尔值和枚举值。除此之外，由属性页或对话框来完成其它的属性操作。但是，VS .NET 设计器允许组件设计器来定义属性浏览器如何处理对象的任何属性类型，以及用户如何编辑这些类型。</p>
<p>.NET Framework 使用这些编辑器编辑许多内置类型，例如，System.Drawing.Color、System.Drawing.Font 或 Win Forms 控件的<strong>Dock</strong>和<strong>Anchor</strong>属性。</p>
<p><img alt="" src="http://www.microsoft.com/china/msdn/Archives/library/techart/images/pdc_vsdescmp1a.gif" border=0></p>
<p><img alt="" src="http://www.microsoft.com/china/msdn/Archives/library/techart/images/pdc_vsdescmp1b.gif" border=0></p>
<p><img alt="" src="http://www.microsoft.com/china/msdn/Archives/library/techart/images/pdc_vsdescmp1c.gif" border=0></p>
<p class=label><strong>图 1. 属性编辑器</strong></p>
<p>此属性浏览器可扩展架构来提供四种基本功能类型：
<ul type=disc>
    <li>值转换<br><br>
    <li>子属性<br><br>
    <li>枚举<br><br>
    <li>下拉/弹出编辑器 </li>
</ul>
<p>System.ComponentModel.TypeConverter 处理前三个功能类型，源自System.WinForms.Design.UITypeEditor 的编辑器处理最后一个。</p>
<p>所有在 .NET Framework 中遇到的属性都有内置的 TypeConverters。让我们先看一下 TypeConverter 类及其功能，然后再讨论特定的示例。下面的代码是 TypeConverter 的缩减版。它具有许多已列出方法的特色，但是为了简单起见，将它们忽略，只有那些核心方法被保留下来。所有此处列出的方法都是虚拟的，因此可在需要的时候忽略它们。但是，TypeConverter 是作为一个基类而不是接口来实现的，大多数重要功能已经内置，用户可以忽略与应用程序相关的部分。</p>
<pre><code>public class TypeConverter {
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 值转换方法。
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 确定 TypeConverter 可以从特定类型转换为目标类型。
public virtual bool CanConvertFrom(
ITypeDescriptorContext context,
Type sourceType);
&nbsp;&nbsp;&nbsp;// 确定 TypeConverter 是否可以从目标类型转换为特定类型。
public virtual bool CanConvertTo(
ITypeDescriptorContext context,
Type destinationType);
&nbsp;&nbsp;&nbsp;// 为 TypeConverter 转换目标类型值中的值。
public virtual object ConvertFrom(
ITypeDescriptorContext context,
object value,
object[] arguments);
&nbsp;&nbsp;&nbsp;// 将值从 TypeConverters 目标类型转换为目的类型。
public virtual object ConvertTo(
ITypeDescriptorContext context,
object value,
Type destinationType,
object[] arguments);
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 实例创建方法。
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;// 创建目标类型的对象。
public virtual object CreateInstance(
ITypeDescriptorContext context,
PersistInfo persistInfo);
&nbsp;&nbsp;&nbsp;// 创建目标类型的对象并从给定的 IDictionary 置入它的值。
&nbsp;&nbsp;&nbsp;
public virtual object CreateInstance(
ITypeDescriptorContext context,
IDictionary propertyValues);
&nbsp;&nbsp;&nbsp;// 指定 TypeConverter 是否知道如何创建目标类型的对象实例。
&nbsp;&nbsp;&nbsp;
public virtual bool GetCreateInstanceSupported(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 获得值的 PersistInfo，
// PersistInfo 是说明给定值保留状态的对象。
&nbsp;&nbsp;&nbsp;
public virtual PersistInfo GetPersistInfo(
ITypeDescriptorContext context,
object value);
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 子属性方法。
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 检索确实要在属性浏览器中为此类型显示的任何子属性。
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
public virtual PropertyDescriptorCollection GetProperties(
ITypeDescriptorContext context,
object value,
MemberAttribute[] attributes);
&nbsp;&nbsp;&nbsp;// 指定此类型是否应该在属性浏览器中与 '+' 一同显示，且是否显示子属性。
public virtual bool GetPropertiesSupported(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 预定义的/标准值方法。
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 为此类型检索一个预定义的&#8220;标准&#8221;值集合。
public virtual StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 指定 GetStandardValues 的值集合是一个可能或有效值的完整列表。
&nbsp;&nbsp;&nbsp;
public virtual bool GetStandardValuesExclusive(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 确定 TypeConverter 是否可以返回一个标准值列表。
public virtual bool GetStandardValuesSupported(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 检查值对于此类型是否是有效值。
public virtual bool IsValid(
ITypeDescriptorContext context, object value);
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;// 实用方法。
&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;// 按名称数组指定的属性顺序排列属性集合。
&nbsp;&nbsp;&nbsp;
protected PropertyDescriptorCollection SortProperties(
PropertyDescriptorCollection props,
string[] names);
}
public interface ITypeDescriptorContext : IServiceObjectProvider {
&nbsp;&nbsp;&nbsp;// 返回符合此上下文环境的 IContainer。
IContainer Container { get; }
&nbsp;&nbsp;&nbsp;// 返回正在检查的实例。
// 如果有值传递给 TypeConverter，
// ITypeDescriptorContext::Instance 将返回提供值的对象。
&nbsp;&nbsp;&nbsp;
object Instance { get; }
// 在对从实例返回的对象更改前调用。如果返回 False, 则不能做出更改并应中止。
bool OnComponentChanging();
&nbsp;&nbsp;&nbsp;// 在更改后，或已经对从实例返回的对象更改后调用。
void OnComponentChanged();
}
</code></pre>
<p>正如您看到的那样，TypeConverter 包含很多内容。还包括 <strong>ITypeDescriptorContext </strong>的一个简要说明。因为它在大多数 TypeConverter 方法中以一个参数形式出现。<strong> ITypeDescriptorContext </strong>允许对服务和组件对象进行访问，这些组件对象提供传递给 TypeConverter 的所有值。</p>
<p>但是，仔细研究 TypeConverter 会发现它并不象表现的那样复杂。其功能可分为四组：
<ul type=disc>
    <li><strong>值转换：</strong>诸如属性浏览器的客户机需要经常将一种字符串表示方式方便地转换为另一种。TypeConverter 为此提供了一种标准方法，以及一种判定给定转换是否可行的方法。因为字符串之间的转换最为常见，所以 TypeConvert 为 <em>ConvertFromString</em> 和 <em>ConvertToString</em>（未显示）提供了更有益的方式。 <br><br>
    <li><strong>实例的创建与持续性：</strong>TypeConverter 负责创建给定类型的实例。TypeConverter 可使类型的创建者来控制这一过程，而不是强制客户机来检查构造器或获知默认值。按此方式，TypeConverter 也扮演在设计器中通过代码保持组件的角色。它可以创建与使用<strong> PersistInfo </strong>对象，该对象说明对象状态的哪一部分与持续性相关（经常取自代码）以及如何保持它们。<br><br>
    <li><strong>子属性方法：</strong>TypeConverter 允许对象对在属性浏览器中显示的子属性进行控制。<strong>System.Drawing.Font</strong> 就是一例，实例展开时显示的属性并不完全匹配 <strong>Font</strong> 对象本身的实际属性。它们为明确性和可用性而被更改并重新排序。<br><br>
    <li><strong>标准值：</strong>许多类型（如枚举）都具有定义的值子集。其它类型可以有预定义的全集，但也可以接受其它数值。TypeConverter 允许客户机检查这些列表并检查未在列表中的值是否可以接受。 </li>
</ul>
<p>TypeConverter 可以通过两种方式之一与属性相关联。通过在类声明中有 TypeConverterAttribute，类型可以指定它们自己的 TypeConverter。通常这些类让它们的转换器成为嵌套类，因此类型和设计时的信息是一个整体。换句话说，属性也可以通过属性声明本身指定 TypeConverter 来忽略与属性类型相关的 TypeConverter。当然在那里指定的 TypeConverter 必须要知晓特定类型。不能任意指定 TypeConverter，但是这种方法有助于了解给定属性的显示方式，添加或删除子属性或为不同字符串转换添加支持。您可以从默认的 TypeConveter 中导出一种类型，并将导出的类型指定为给定属性的 TypeConverter 来了解这些知识。与属性浏览器接口的另一半涉及属性编辑器。属性编辑器的结构远比 TypeConverter 结构简单。为了给不依赖于 Win Forms 或不与其连接的编辑器保持开放，并没有基本编辑器类型。因为编辑器通常采用用户界面（UI）驱动，.NET Framework 中的所有标准编辑器都源自 System.Drawing.Design.UITypeEditor。</p>
<pre><code>public class UITypeEditor {
&nbsp;&nbsp;&nbsp;// 在编辑值时调用。
public virtual object EditValue(
ITypeDescriptorContext context, IServiceObjectProvider provider,
object value);
&nbsp;&nbsp;&nbsp;// 指定此编辑器是否可以显示值。
public virtual bool GetPaintValueSupported(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 如果有，则返回此编辑器的用户界面样式。
public virtual UITypeEditorEditStyle GetEditStyle(
ITypeDescriptorContext context);
&nbsp;&nbsp;&nbsp;// 当客户机需要在 UI 上显示值时调用。
public virtual void PaintValue(
ITypeDescriptorContext context,
object value,
Graphics canvas,
Rectangle rectangle);
}
public enum UITypeEditorEditStyle {
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;// 没有交互的 UI 组件。
None = 1,
&nbsp;&nbsp;&nbsp;// 模态 UI 属性将显示一个 [...]
// 来启动一个对话。
Modal = 2,
&nbsp;&nbsp;&nbsp;// 下拉 UI 属性将显示向下箭头的按钮，并且
&nbsp;&nbsp;&nbsp;// UI 将处于类似组合框的下拉菜单中。
DropDown = 3
}
// 接口可通过调用 UITypeEditor::EditValue 中的
// provider.GetServiceObject(typeof(IwinFormsEditorService))
// 进行检索。
public interface IWinFormsEditorService {
&nbsp;&nbsp;&nbsp;// 关闭当前显示的下拉菜单。
void CloseDropDown();
&nbsp;&nbsp;&nbsp;// 为值的 UI 编辑将给定控件
// 置于下拉菜单中。
void DropDownControl(Control control);
&nbsp;&nbsp;&nbsp;// 启动模态对话框来编辑属性值
DialogResult ShowDialog(Form dialog);
}
</code></pre>
<p>EditorAttribute 与 TypeConverterAttribute 类似, 可在它们应用的类型或某一特定类型的属性上予以指定，该特定类型将取代在 Type 本身中指定的任何值。</p>
<p>用户单击属性浏览器上的下拉或椭圆按钮将调用 UITypeEditor.EditValue。这里是在下拉编辑器中 EditValue 的一个典型实现：</p>
<pre><code>public override object EditValue(
ITypeDescriptorContext context, IServiceObjectProvider provider,
object value) {
object returnValue = value;
if (provider != null) {
IWinFormsEditorService edSvc = (IWinFormsEditorService)
provider.GetServiceObject(
typeof(IWinFormsEditorService));
if (edSvc != null) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyUIEditorControl uiEditor =
new MyUIEditorControl(edSvc);
edSvc.DropDownControl(uiEditor);
value = uiEditor.NewValue;
}
}
return value;
}
</code></pre>
<p>PaintValue 允许编辑器显示可见的特定值的表示。例如，<strong>WinForms</strong> 对象使用该编辑器编辑图像、颜色和字体。</p>
<p><img alt="" src="http://www.microsoft.com/china/msdn/Archives/library/techart/images/pdc_vsdescmp2.gif" border=0></p>
<p class=label><strong>图 2. 编辑器显示</strong></p>
<p>这是一个基于代码的示例：</p>
<pre><code>public override void PaintValue(
ITypeDescriptorContext context,
object value,
Graphics canvas,
Rectangle rectangle) {
if (value is Color) {
Color color = (Color)value;
SolidBrush b = new SolidBrush(color);
canvas.FillRectangle(b, rectangle);
b.Dispose();
}
}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      ColorEditor's PaintValue code.
</code></pre>
<h2><a name=pdc_vsdescmp_topic5></a>从外部引入：代码持续性</h2>
<p>与一些过去的设计器不同，.NET Framework 组件的 Win Forms 和其它 VS .NET 设计器只依赖于代码持续性来形成状态。没有不可思议的格式，没有隐藏的数据，只有简明的代码。当然，类似位图和本地化字符串之类的内容会以二进制的形式与代码打包，但是组件的状态及其组成物在代码中保持连续。在设计器中做改动时会生成代码。如果您处理代码，它会被重新分析，更改也会反映到设计器中。</p>
<p>为了充分利用这一功能，.NET Framework 中的设计器提供了所有的渠道。对于任何类型，所有设计器都要了解以下内容：
<ol>
    <li>对于持续性，对象状态的什么信息有意义？<br><br>
    <li>信息怎样返回给活动对象？ </li>
</ol>
<p>这些已经在前面章节中进行了简要的论述。我们再次强调 TypeConverter 是过程的核心。代码生成和代码分析设计器涉及一种称为 <strong>CreationBundle</strong> 的特别类型的 <strong>PersistInfo</strong>。例如：</p>
<pre><code>[TypeConverter(<span class=cfe><strong>typeof</strong></span>(IntBoolString.IntBoolStringConverter))]
&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public class</strong>
</span>IntBoolString {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private int</strong></span> intVal;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private string</strong></span> stringVal;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private bool</strong></span> boolVal;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public </strong></span>IntBoolString(<span class=cfe><strong>string</strong></span> s, <span class=cfe><strong>int</strong></span> I, <span class=cfe><strong>bool</strong></span> b) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This.intVal = I;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This.stringVal =s ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This.boolVal = b;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public bool</strong></span> Bool{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {<span class=cfe><strong>return</strong></span> boolVal;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>set</strong></span> {boolVal = <span class=cfe><strong>value</strong></span>;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public int</strong></span> Int {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {<span class=cfe><strong>return</strong></span> intVal;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>set</strong></span> {intVal = <span class=cfe><strong>value</strong></span>;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public string</strong></span> String {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get </strong></span>{<span class=cfe><strong>return</strong></span> stringVal;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>set</strong></span> {stringVal = <span class=cfe><strong>value</strong></span>;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override string</strong></span> ToString() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> intVal + "," + boolVal + "," + stringVal;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public class</strong></span> IntBoolStringConverter : TypeConverter {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override bool</strong></span> CanConvertFrom(
ITypeDescriptorContext context,
Type sourceType) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> (sourcetType == <span class=cfe><strong>typeof</strong></span>(<span class=cfe><strong>string</strong></span>));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
<span class=cfe><strong>public virtual object</strong></span> ConvertFrom(
ITypeDescriptorContext context,
<span class=cfe><strong>object value</strong></span>,
<span class=cfe><strong>object</strong></span>[] arguments) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if </strong></span>(<span class=cfe><strong>value is string</strong></span>) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>string</strong></span> stringValue = (<span class=cfe><strong>string</strong></span>)<span class=cfe><strong>value</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>int</strong></span>  intValue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>bool</strong></span> boolValue;
<span class=cfe><strong>int </strong></span>commaIndex =
stringValue.IndexOf(',');
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if</strong></span> (commaIndex != -1) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;intValue = Int32.
Parse(stringValue.
Substring(0, commaIndex));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;commaIndex = stringValue.
IndexOf(',',
commaIndex + 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if</strong></span> (commaIndex != -1) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>int</strong></span> nextComma = stringValue.IndexOf(',', commaIndex + 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if</strong></span> (nextComma != -1) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolValue = Boolean.Parse(stringValue.Substring<br>(commaIndex+1,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nextComma - commaIndex));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringValue = stringValue.Substring(nextComma+1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return new</strong></span> IntBoolString(intVal, boolVal, stringValue);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>throw new </strong></span>FormatException("Can't convert"' + stringValue +<br> "' to IntBoolString Object");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override</strong></span> PersistInfo GetPersistInfo(ITypeDescriptorContext <br>context, <span class=cfe><br><strong>object value</strong></span>) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if</strong></span> (<span class=cfe><strong>value is</strong></span> IntBoolString) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IntBoolString ibs = (IntBoolString)<span class=cfe><strong>value</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return new</strong></span> CreationBundle(<span class=cfe><strong>typeof</strong></span>(IntBoolString), <span class=cfe><strong>null</strong></span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>new</strong></span> CreationArgument[] {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>new</strong></span> CreationArgument(ibs.Int, <span class=cfe><strong>typeof</strong></span>(Int32)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>new</strong></span> CreationArgument(ibs.Bool, <span class=cfe><strong>typeof</strong></span>(<span class=cfe><strong>bool</strong></span>)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>new</strong></span> CreationArgument(ibs.String, <span class=cfe><strong>typeof</strong></span>(<span class=cfe><strong>string</strong></span>))});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return base</strong></span>.GetPersistInfo(context, <span class=cfe><strong>value</strong></span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override object</strong></span> CreateInstance(ITypeDescriptorContext
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context, IDictionary propertyValues) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return new</strong></span> IntBoolString((<span class=cfe><strong>int</strong></span>)propertyValues["Int"],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span class=cfe><strong>bool</strong></span>)propertyValues["Bool"],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span class=cfe><strong>string</strong></span>)propertyValue["String"]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override bool</strong></span> GetCreateInstanceSupported(ITypeDescriptorContext <br>context) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return true</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;}
</code></pre>
<p>使用 <strong>CreationBundle</strong> 对象的好处是，如果构建器与传递来的 CreationArguments 的每一类型都匹配，那么该对象知道如何创建存储信息的对象。当调用 TypeConverter::CreateInstance 并试图以这种方式创建和初始化对象时，TypeConverter 的默认实现调用 CreationBundle::Invoke。如果没有构建器，CreateInstance 调用会使用 <strong>IDictionary</strong> 定制更为灵活的对象创建。传入的 <strong>IDictionary </strong>对象包含每一个属性名的非持续性值。</p>
<p>组件经常包含组成多个对象的属性值。其它框架经常为此目的使用属性数组。但使用数组也有一些缺点。例如，调入和调出数组时都需要复制数组，这会带来性能问题。数组也不能对添加、修改、或删除值等操作提供智能通知。实际上，如果属性传回数组，则还要进行大量的添加和删除项目的工作。数组还是一个快照值，并且在基本对象改变时也不会更新。</p>
<p>与此不同，.NET Framework 为此目的使用集合，集合是实现 <strong>ICollection</strong> 的对象。对象可以创建集合并将其传给任一对象，基本对象的任何改动都会使引用更新。如果集合被其它对象更改，也会通知该对象。为了使 .NET Framework 设计器使用集合，它们还需要支持 <strong>ALL</strong> 属性，该属性有 &#8220;get&#8221; 和 &#8220;set&#8221;，它的类型是一个集合持有的对象数组。例如：</p>
<pre><code>&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public class</strong></span> IntCollection : ICollection {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private int</strong></span>[] values;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public</strong></span> IntCollection(<span class=cfe><strong>int</strong></span>[] intValues) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>this</strong></span>.values = (<span class=cfe><strong>int</strong></span>[])intValues.Clone();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public int</strong></span>[] All {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return </strong></span>(<span class=cfe><strong>int</strong></span>[])values.Clone();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>set</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;values  = (<span class=cfe><strong>int</strong></span>[])<span class=cfe><strong>value</strong></span>.Clone();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public int</strong></span> Count {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if </strong></span>(values == <span class=cfe><strong>null</strong></span>)  {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> values.Length;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Browsable(<span class=cfe><strong>false</strong></span>)]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public object</strong></span> SyncRoot {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return this</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Browsable(<span class=cfe><strong>false</strong></span>)]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public bool</strong></span> IsReadOnly {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return false</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Browsable(<span class=cfe><strong>false</strong></span>)]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public bool</strong></span> IsSynchronized {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return true</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;}
</code></pre>
<p>.NET Framework 持续性机制可以使集合保持或解除持续性。如果集合由更高级的类型组成，例如上面的 BoolInString 示例类型，那么只需让与该类型关联的 TypeConverter 为集合中的每一项创建一个有效的 <strong>PersistInfo</strong> (特别是为 VS .NET 设计器创建 <strong>CreationBundle</strong>)。</p>
<h2><a name=pdc_vsdescmp_topic6></a>组件设计器</h2>
<p>如前所述，.NET Framework 内置的设计器可以满足大多数主要组件的要求。尽管这样，.NET Framework 为组件设计者提供了一个可充分扩展的体系结构。所有设计器都基于下面的 <strong>System.ComponentModel.Design.IDesigner</strong> 接口：</p>
<pre><code>public interface IDesigner {
// 与此设计器相关联的组件。
IComponent Component {get;}
// 与此组件相关联的设计时动词，
// 例如用于 TabControl 的&#8220;添加标签&#8221;。
DesignerVerb[] Verbs {get;}
// 分配设计器使用的资源，
// 设计器在此调用后不再可用。
void Dispose();
// 调用使设计器执行&#8220;默认操作&#8221;，
// 通常为响应运行时对组件的双击而调用。
void DoDefaultAction();
// 用给定组件初始化设计器。
void Initialize(IComponent component);
}
</code></pre>
<p>正如您所看到的那样，<strong>IDesigner</strong> 是直接的。设计者通过 DesignerAttribute 与组件关联：</p>
<pre><code> [Designer("MyNameSpace.Design.MyComponentDesigner, MyNameSpace.DLL")]
Public class MyComponent : Component {
}
</code></pre>
<p>如果 DesignerAttribute 不出现在类中，则类分层结构将被遍历直至找到开发环境为止。在前面的示例中，将默认的 ComponentDesigner 定位在 Component 基类上，然后会使用它。一些设计器使用 UI。而另一些则不然。对于 ComponentDesigner，您会看到一个表示对象的图标，因为组件通常没有 UI。另一方面，Win Forms 控件具有在设计时显示实际控件的设计器。</p>
<p><img alt="" src="http://www.microsoft.com/china/msdn/Archives/library/techart/images/pdc_vsdescmp3.gif" border=0></p>
<p class=label><strong>图 3. 设计时的 Win Form 控件</strong></p>
<p>请注意不显示 UI 的控件就是设计器下面的图标，拥有 UI 的 Win Forms 控件显示在窗体设计器中，这与运行时一样。所有这些都构建在 <strong>IDesigner</strong> 基础上。通常，控件的设计器会控制其正在设计的控件的</strong> WindowProc</strong>（取自 System.WinForms.Design.ControlDesigner 的设计器可以通过简单地覆盖 </strong>WindowProc</strong> 方法来完成此项任务）来执行诸如命中测试这类的复杂任务。然而，对于大多数组件来说，内置的默认设计器应该就足够了。</p>
<h2><a name=pdc_vsdescmp_topic7></a>访问设计器服务和基础结构</h2>
<p>VS .NET 中的 .NET Framework 设计器开发环境的许多服务和基础结构组件简化了复杂的操作，使设计器各部分之间可以互通状态信息。这些服务总是通过使用 <strong>GetServiceObject</strong> 方法的 <strong>IServiceObjectProvider</strong> 来进行访问。这是一些有趣的设计器服务列表：</p>
<table cols=2 cellPadding=5 rules=rows border=1 frame=below>
    <tbody>
        <tr vAlign=top>
            <td class=label width="46%"><font class=90v><strong>类型</strong>
            <p class=label></p>
            </font></td>
            <td class=label width="54%"><font class=90v><strong>说明</strong></font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IDesignerHost</strong></font></td>
            <td width="54%"><font class=90v>与任何顶级设计器相关联的主类。提供方法以添加服务，创建和安放组件，删除组件，以及将操作批处理化。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IComponentChangeService</strong></font></td>
            <td width="54%"><font class=90v>在添加、删除、重命名、或修改组件时提供通知。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>ISelectionService</strong></font></td>
            <td width="54%"><font class=90v>设置或获取当前在设计器中选择的项目。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IToolboxService</strong></font></td>
            <td width="54%"><font class=90v>允许检查或修改工具框上的项目以及它们的选择状态等等。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IUndoService</strong></font></td>
            <td width="54%"><font class=90v>提供创建操作的撤销/重做单元的工具，并管理撤销/重做栈。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IHelpService</strong></font></td>
            <td width="54%"><font class=90v>允许设置帮助主题或调用帮助项目</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IMenuCommandService</strong></font></td>
            <td width="54%"><font class=90v>允许处理设计器菜单命令和动词。</font></td>
        </tr>
        <tr vAlign=top>
            <td width="46%"><font class=90v><strong>IReferenceService</strong></font></td>
            <td width="54%"><font class=90v>将设计器引用映射到对象。例如，命名组件&#8220;button 1&#8221;。</font></td>
        </tr>
    </tbody>
</table>
<br>
<p><strong>IDesignerHost</strong> 是 VS .NET 中所有设计器的基础。<strong>IDesignerHost</strong> 也是一个 <strong>IServiceObjectProvider</strong>，它可以动态地添加或删除服务。它还提供方法来创建组件以确保正确安放组件。这里是一个使用 <strong>IDesignerHost</strong> 创建组件的示例： </p>
<pre><code>Public class MyDesigner : IDesigner {
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;//  .
Private void OnSurfaceDoubleClick(object s, EventArgs e) {
&nbsp;&nbsp;&nbsp;IDesignerHost host =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(IDesignerHost)this.Component.Site.GetServiceObject(typeof(IDesignerHost));
If (host != null) {
&nbsp;&nbsp;&nbsp;Object newComponent = host.CreateComponent(typeof(MyComponent));
&nbsp;&nbsp;&nbsp;DoSomethingInterestingWithComponent(newComponent);
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}
&nbsp;&nbsp;&nbsp;// ...
</code></pre>
<h2><a name=pdc_vsdescmp_topic8></a>为组件授予许可证</h2>
<p>在 .NET Framework 的可扩展模型中，授予许可证的体系结构也可以扩展。为了简便使用，框架定义了一个内置的、标准许可证授予方法，以控制组件是否许可在设计时使用，但开发人员可以用任何他们认为恰当的方式替换此方案。</p>
<pre><code>// 向控件添加 LicenseProviderAttribute。
LicenseProvider(typeof(LicFileLicenseProvider))]
public class MyControl : RichControl {
// 创建新的空许可证。
private License license = null;
public MyControl () {
// 向控件的构建器添加验证。
license = LicenseManager.Validate(typeof(MyControl), this);
// 执行其它实例化任务...  }
public override void Dispose() {
if (license != null) {
license.Dispose();
license = null;
}
}
protected override void Finalize() {
Dispose();
base.Finalize();
}
}
</code></pre>
<p>此示例使用内置的许可支持来授予许可证。LicFieLicenseProvider 只是在与类组合体相同的目录中，寻找一个名为 <em>&lt;classname&gt;.lic</em> 的文件，其中 <em>classname</em> 是类型全名。String 类型的名称是 <em>System.String.lic</em>。该文件包含字符串&#8220;System.String 是一个许可的组件&#8221;。如果找到该文件，LicenseManager.Validate 会返回一个 <strong>License</strong> 对象，它将与类实例一同分配。</p>
<p>实现您自己的许可证方案也很容易。只需创建您自己的源于 LicenseProvider 的类并实现自己的 <strong>GetLicense</strong> 方法。您可能要实现一个基于注册表的许可方案，其中许可的设计时组件都在注册表中有一个条目：</p>
<pre><code><span class=cfe><strong>public class</strong></span> RegistryLicenseProvider: LicenseProvider {
<span class=cfe><strong>public override</strong></span> License GetLicense(
LicenseContext context,
Type type,
<span class=cfe><strong>object</strong></span> instance,
<span class=cfe><strong>bool </strong></span>allowExceptions) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RegistryKey licenseKey = Registry.LocalMachine.
OpenSubKey("Software\\MyCompany\\ComponentLicenses");
<span class=cfe><strong>if</strong></span> (context.UsageMode == LicenseUsageMode.Designtime) {
<span class=cfe><strong>if </strong></span>(licenseKey != <span class=cfe><strong>null</strong></span> &amp;&amp; licenseKey.GetValue(type.FullName) != <span class=cfe><strong>null</strong></span>) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return new</strong></span> RegLicense(<span class=cfe><strong>this</strong></span>, type);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>if</strong></span> (allowExceptions) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>throw new</strong></span> LicenseException(type, instance,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Couldn''t get design-time license for ''"" +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type.FullName + "''");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return null</strong></span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;<span class=cfe><strong>else</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return new</strong></span> RuntimeRegLicense(<span class=cfe><strong>this</strong></span>, type);
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private class</strong></span> RuntimeRegLicense : License {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public string</strong></span> LicenseKey {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> type.FullName;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override void</strong></span> Dispose() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private class</strong></span> RegLicense : License {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private </strong></span>RegistryLicenseProvider owner;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>private</strong></span> Type type;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public</strong></span> RegLicense(RegistryLicenseProvider owner, Type type) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>this</strong></span>.owner = owner;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>this.</strong></span>type = type;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public string</strong></span> LicenseKey {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>get</strong></span> {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>return</strong></span> type.FullName;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public override void</strong></span> Dispose() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[LicenseProvider(<span class=cfe><strong>typeof</strong></span>(RegistryLicenseProvider))]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class=cfe><strong>public class</strong></span> MyControl : Control {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
</code></pre>
<p>使用该许可证的组件将在注册表的以下位置中有一个条目：</p>
<pre><code>HKEY_LOCAL_MACHINE\Software\MyCompany\ComponentLicenses
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Type full name&gt;="true"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</code></pre>
<h2><a name=pdc_vsdescmp_topic9></a>结论</h2>
<p>以管理代码编写控件远远优于传统的 C++/COM 方法。Mircosoft 对从琐碎的编程到 C，从通用语言运行时到重组的 Visual Basic 语言进行根本性的工程改进，使可能遇到的故障大大减少。Mircosoft .NET Framework 是用这些技术与原理来开发的第一个代码集，对集成设计器的支持是这一方法的关键。</p>
<img src ="http://www.blogjava.net/superwei/aggbug/124685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-06-16 20:32 <a href="http://www.blogjava.net/superwei/articles/124685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 一些数据库连接字符串写法 </title><link>http://www.blogjava.net/superwei/articles/123787.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Wed, 13 Jun 2007 02:07:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/123787.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/123787.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/123787.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/123787.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/123787.html</trackback:ping><description><![CDATA[<div class=postTitle><a href="http://blog.csdn.net/ericfine/archive/2004/08/05/66543.aspx"><img height=13 src="http://blog.csdn.net/images/authorship.gif" width=15 border=0><u><font color=#800080>&nbsp;一些数据库连接字符串写法</font></u></a> </div>
<div class=postText>
<p><strong>ODBC链接</strong></p>
<p>
<table cellSpacing=1 cellPadding=3 width="100%" bgColor=#999999 border=0>
    <tbody>
        <tr bgColor=#ffffff>
            <td>
            <div align=center>适合数据库类型</div>
            </td>
            <td>
            <div align=center>链接方式</div>
            </td>
        </tr>
        <tr bgColor=#ffffff>
            <td width="27%">access</td>
            <td width="73%">"Driver={microsoft access driver(*.mdb)};dbq=*.mdb;uid=admin;pwd=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>dBase </td>
            <td>"Driver={microsoft dbase driver(*.dbf)};driverid=277;dbq=------------;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>Oracle </td>
            <td>"Driver={microsoft odbc for oracle};server=oraclesever.world;uid=admin;pwd=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>MSSQL server</td>
            <td>"Driver={sql server};server=servername;database=dbname;uid=sa;pwd=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>MS text </td>
            <td>"Driver={microsoft text driver(*.txt; *.csv)};dbq=-----;extensions=asc,csv,tab,txt;Persist SecurityInfo=false;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>Visual Foxpro</td>
            <td>"Driver={microsoft Visual Foxpro driver};sourcetype=DBC;sourceDB=*.dbc;Exclusive=No;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>MySQL</td>
            <td>"Driver={mysql};database=yourdatabase;uid=username;pwd=yourpassword;option=16386;"</td>
        </tr>
    </tbody>
</table>
<p><strong>OLEDB链接</strong></p>
<table cellSpacing=1 cellPadding=3 width="100%" bgColor=#999999 border=0 dwcopytype="CopyTableRow">
    <tbody>
        <tr bgColor=#ffffff>
            <td>
            <div align=center>适合的数据库类型</div>
            </td>
            <td>
            <div align=center>链接方式</div>
            </td>
        </tr>
        <tr bgColor=#ffffff>
            <td width="27%">access</td>
            <td width="73%">"Provider=microsoft.jet.oledb.4.0;data source=your_database_path;user id=admin;password=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>Oracle</td>
            <td>"Provider=OraOLEDB.Oracle;data source=dbname;user id=admin;password=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>MS SQL Server</td>
            <td>"Provider=SQLOLEDB;data source=machinename;initial catalog=dbname;userid=sa;password=pass;"</td>
        </tr>
        <tr bgColor=#ffffff>
            <td>MS text </td>
            <td>"Provider=microsof.jet.oledb.4.0;data source=your_path;Extended Properties'text;FMT=Delimited'"</td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.blogjava.net/superwei/aggbug/123787.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-06-13 10:07 <a href="http://www.blogjava.net/superwei/articles/123787.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.NET环境下log处理解决方案(Log4Net) </title><link>http://www.blogjava.net/superwei/articles/119191.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 22 May 2007 09:43:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/119191.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/119191.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/119191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/119191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/119191.html</trackback:ping><description><![CDATA[<h1>载自：<a href="http://blog.csdn.net/zyc21st/archive/2006/03/27/639820.aspx"><u>http://blog.csdn.net/zyc21st/archive/2006/03/27/639820.aspx</u></a><a name=_Toc90810602><strong><span><font size=3><br>1 </font></span></strong></a><font size=3><span><strong><span>实现目的</span></strong></span><strong><span>&nbsp;&nbsp;</span></strong></font></h1>
<p><span>一个好的应用系统离不开<span>log</span>文件的支持，所以<span>log</span>文件的处理在应用系统的开发过程中是占很大比重的。<span>log</span>文件的处理方法有很多种，采用一种功能强大且方法简单的处理方法，不但能保证<span>log</span>文件的输出质量，更能大大缩短系统开发和维护的周期。本文介绍的<span>.net</span>环境下<span>log</span>文件处理解决方案，采用的是开源日志框架<span>log4net</span>。本文介绍的<span>log4net</span>基于<span>log4net-1.2.0</span>版本。</span>&nbsp;</p>
<h1><a name=_Toc90810603><strong><span><font size=3>2&nbsp;log4net</font></span></strong></a><font size=3><span><strong><span>概述</span></strong></span><span>&nbsp;&nbsp;</span></font></h1>
<p><span>log4net</span><span>是一个辅助应用系统输出多种样式<span>log</span>信息的日志框架，它是著名的<span>log4j</span>框架在<span>.net</span>环境下的实现，在实际应用中，可以采用和<span>log4j</span>类似的方法在<span>.net</span>环境下构建起一个功能强大的<span>log</span>处理系统。<span>log4net</span>和应用系统的结合不需要修改应用系统本身的代码，它们之间的绑定非常简单，并且<span>log4net</span>处理<span>log</span>信息是和应用系统同步的，其处理速度和输出的多样性也是其它任何<span>log</span>处理系统无法比拟的。<span>log4net</span>最有特色的地方是，它对<span>log</span>进行了分等级处理，这样应用系统可以在实际运行环境下很简单的输出不同级别的<span>log</span>信息，帮助系统开发维护人员在最快的时间里得到最有用的系统<span>log</span>信息。</span>&nbsp;</p>
<p><span>log4net</span><span>处理<span>log</span>文件的方法是灵活多变的，它可以将<span>log</span>信息输出不同的样式和输出到不同的地方，如控制台（<span>Console</span>），文本文件，<span>XML</span>文件，以流的形式发送到其它地方（如数据库），发送到电子邮件中，根据天数或者文件大小产生新的文件等等。并且开发人员可以根据自己的喜好格式化<span>log</span>输出，如自由指定样式，可以包含<span>log</span>级别与<span>log</span>信息，包含<span>log</span>时间、线程、类别等信息的样式等等。</span>&nbsp;</p>
<p><span>本文以最常见的<span>log</span>输出形式：输出到文本文件和数据库作为范例来介绍<span>log4net</span>的使用方法</span><font size=3><span>。</span></font>&nbsp;</p>
<h1><a name=_Toc90810604><strong><span><font size=3>3&nbsp;log4net </font></span></strong></a><font size=3><span><strong><span>主要结构分析</span></strong></span><strong><span>&nbsp;&nbsp;</span></strong></font></h1>
<p><span>在使用<span>log4net</span>之前先了解<span>log4net</span>的结构是非常有必要的，下面简要分析<span>log4net</span>结构中重要的几个部分。</span>&nbsp;</p>
<h2><a name=_Toc90810605><strong><span>3.1 Logger</span></strong></a><strong>&nbsp;</strong><span>Logger</span><span>是应用程序与<span>log4net</span>交互的主要模块，它也是<span>log4net</span>生成<span>log</span>的模块。<span>Logger</span>主要负责得到<span>log</span>信息，得到<span>log</span>信息之后接下来的显示<span>log</span>则是在<span>Layout</span>模块中处理。</span>&nbsp;</h2>
<h3><a name=_Toc90810606></a><span><span>3.1.1</span></span><span><span> Logger</span></span><span><span>的管理方式</span></span><span>
<p>&nbsp;</p>
</span><span>Logger</span><span>提供了多种方法用于记录任何类型的<span>log</span>信息，可以在应用程序中使用多个<span>Logger</span>实例，而把它们的维护工作交给<span>log4net</span>框架作为&#8220;命名实体<span>(Named Entity)</span>&#8221;进行维护。这就意味着，不再需要将应用程序中生成的<span>Logger</span>实例作为参数在应用程序中传递，以便应用程序中的其它模块能使用该实例。唯一需要做的就是通过<span>Logger</span>的命名来调用它。</span></h3>
<p><span>目前，<span>log4net</span>使用类似<span>.net</span>命名空间的方式管理命名实体。例如，有两个<span>Logger</span>，分别定义为<span>logger.First</span>以及<span>logger.First.One</span>。那么这两个是不同的<span>Logger</span>，而且<span>logger.First</span>是<span>logger.First.One</span>的祖先，同时<span>logger.First.One</span>继承了<span>logger.First</span>的属性。位于命名空间最高层的<span>Logger</span>是默认<span>Logger</span>，也被称为根<span>Logger</span>（<span>Root Logger</span>）。</span>&nbsp;</p>
<h3><a name=_Toc90810607></a><span><span>3.1.2</span></span><span>&nbsp;</span><span><span>实现自己的<span>Logger</span></span></span><span>
<p>&nbsp;</p>
</span><span>如果要实现自己的<span>Logger</span>，那么<span>log4net</span>提供了接口<span>ILog</span>用于实现自己的<span>Logger</span>。<span>ILog</span>的大致结构如下：</span>&nbsp;</h3>
<pre><span>public interface Ilog
<p>&nbsp;</p>
</span></pre>
<pre><span>{&nbsp;</span></pre>
<pre><span>void Debug(object message); </span></pre>
<pre><span>void Info(object message);</span></pre>
<pre>
<p>&nbsp;</p>
</pre>
<pre><span>void Warn(object message);
<p>&nbsp;</p>
</span></pre>
<pre><span>void Error(object message);
<p>&nbsp;</p>
</span></pre>
<pre><span>void Fatal(object message);
<p>&nbsp;</p>
</span></pre>
<pre><span>void Debug(object message, Exception ex);&nbsp;
<p>&nbsp;</p>
</span></pre>
<pre><span>bool isDebugEnabled;
<p>&nbsp;</p>
</span></pre>
<pre><span>bool </span><span>isInfoEnabled</span><span>; </span></pre>
<pre><span>}</span><span><span>&nbsp;&nbsp;</span></span></pre>
<pre><span></span><span>在<span>Logger</span>中，<span>log4net</span>提供了一个名为<span>LogManager</span>的类给我们用于获取或者创建一个<span>Logger</span>。<span>LogManager</span>提供了方法<span>GetLogger()</span>，该方法接收一个<span>string</span>类型的参数，用于指定<span>Logger</span>的名称：</span>&nbsp;</pre>
<pre><span>log4net.ILog log = log4net.LogManager.GetLogger(</span><span>&#8221;</span><span>Logger_Name</span><span>&#8221;</span><span>)</span><span> ;</span><span>
<p>&nbsp;</p>
</span></pre>
<p><span>当指定名称的<span>Logger</span>不存在时，<span>LogManager</span>将自动创建一个。通常情况下，都是使用<span>Logger</span>所在类的名称来作为<span>Logger</span>的名称，即<span>GetLogger</span>方法的参数可以是<span>typeof(ClassName)</span>。也可以使用如下的方法来传递该参数：</span>&nbsp;</p>
<pre><span>log4net.Ilog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);</span><span>
<p>&nbsp;</p>
</span></pre>
<p><span>尽管上面的代码比<span>typeof(ClassName)</span>要长的多，但是这样做可以在任何一个类中使用相同</span><span>的方法传递参数，从而减少出错的机率。</span>&nbsp;</p>
<h2><strong><span><span>3.2&nbsp;</span></span></strong><strong><span>&nbsp;<a name=_Toc90810608>Logger Level</a></span></strong><span><strong><span>：<span>log</span>级别</span></strong></span><span>&nbsp;&nbsp;</span></h2>
<p><span>从上面的<span>ILog</span>的接口可以看出，<span>log4net</span>有<span>5</span>种不同的记录方式。为什么需要有这<span>5</span>种记录方式呢？其实，这<span>5</span>种记录方式存在不同的优先级别。这些级别定义在<span>log4net.spi.level</span>中。可以根据需要在应用中使用任意一个方法，但是，为了在使用了这些方法之后，在回收时不至于浪费太多的<span>CPU</span>周期。因此，<span>log4net</span>提供了<span>7</span>个等级以及它们各自的布尔属性来节省<span>CPU</span>周期，如下：</span>&nbsp;</p>
<p><span>Logger</span><span>的不同等级：</span>&nbsp;</p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top>
            <p><span>Level</span>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>Allow Method</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>Boolean Property</span></p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>Value</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>OFF</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
            <td vAlign=top>
            <p><span>Highest</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>FATAL</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>void Fatal(...);</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>bool IsFatalEnabled;</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>ERROR</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>void Error(...);</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>bool IsErrorEnabled;</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>WARN</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>void Warn(...);</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>bool IsWarnEnabled;</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>INFO</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>void Info(...);</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>bool IsInfoEnabled;</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>DEBUG</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>void Debug(...);</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p><span>bool IsDebugEnabled;</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>ALL</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
            <td vAlign=top>
            <p>&#160;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font size=3>&nbsp;</font></p>
            </td>
            <td vAlign=top>
            <p><span>Lowest</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>在<span>log4net</span>中，每一个<span>Logger</span>会通过配置文件中的配置信息给予一个优先级别，如果没有指定的优先级别，那么它将尝试从它的父类中继承一个优先级别。</span><span>同样，<span>Logger</span>的每一个方法都有一个预定义的级别，如上表中列出的，<span>Info()</span>方法有着<span>INFO</span>的级别。当运行时，<span>log4net</span>会检查方法的级别以及配置文件赋予<span>Logger</span>的级别，然后执行不同的操作。</span><span>例如，假设某个<span>Logger</span>具有<span>INFO</span>的级别，那么当执行如下操作时：</span>&nbsp;</p>
<pre><span>Logger.Info(&#8220;message&#8221;);
<p>&nbsp;</p>
</span></pre>
<pre><span>Logger.Debug(&#8220;message&#8221;);
<p>&nbsp;</p>
</span></pre>
<pre><span>Logger.Warn(&#8220;message&#8221;);</span><span>
<p>&nbsp;</p>
</span></pre>
<p><span>将会有如下的情况：</span></p>
<p><span><span>1、&nbsp;</span></span><span>方法<span>Info</span>的级别等于<span>Logger</span>的<span>INFO</span>级别，那么<span>Info</span>方法将执行；</span>&nbsp;</p>
<p><span><span>2、&nbsp;</span></span><span>方法<span>Debug</span>的级别低于<span>Logger</span>的<span>INFO</span>级别，那么<span>Debug</span>方法将不执行或抛出异常；</span>&nbsp;</p>
<p><span>3</span><span>、方法<span>Warn</span>的级别高于<span>Logger</span>的<span>INFO</span>级别，那么<span>Warn</span>方法将被执行。</span>&nbsp;</p>
<p><span>由此可以看出，当方法的级别大于或等于<span>Logger</span>的级别时，方法将得以执行。</span>&nbsp;</p>
<p><span>在上表中还定义了两个特殊的级别：<span>ALL</span>和<span>OFF</span>。<span>ALL</span>意味着任何方法都可以执行，<span>OFF</span>则相反。</span><span>为了明确那些操作能够执行，可以用上表中的属性进行判断：</span>&nbsp;</p>
<pre><span>if (logger.IsDebugEnabled){
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;Logger.Debug("message");
<p>&nbsp;</p>
</span></pre>
<pre><span>}
<p>&nbsp;</p>
</span></pre>
<h2><strong><span><span>3.3&nbsp;</span></span></strong><strong><span>&nbsp;<a name=_Toc90810609>Appender</a></span></strong><span><strong><span>：<span>log</span>目的地</span></strong></span><strong><span>&nbsp;&nbsp;</span></strong></h2>
<p><span>每个<span>Logger</span>都可以拥有一个或者多个<span>appender</span>，每个<span>appender</span>表示一个<span>log</span>的输出目的地，比如<span>console</span>、某个文件、数据库甚至电子邮件。可以使用<span>Logger.addAppender(Appender app)</span>为<span>logger</span>增加一个<span>appender</span>；也可以使用<span>Logger.removeAppender(Appender app)</span>为<span>logger</span>移除一个<span>appender</span>。</span>&nbsp;</p>
<p><span>默认情况下，<span>Logger</span>的<span>additive</span>标志被设置为<span>true</span>，表示子<span>Logger</span>将继承父<span>Logger</span>的所有<span>appenders</span>。该选项可以被重置，表示子<span>Logger</span>将不再继承父<span>Logger</span>的<span>appenders</span>。</span>&nbsp;</p>
<p><span>Root Logger</span><span>拥有目标为<span>System.console</span>的<span>consoleAppender</span>，故默认情况下，所有的<span>Logger</span>都将继承该<span>appender</span>。</span>&nbsp;</p>
<h2><strong><span><span>3.4&nbsp;</span></span></strong><strong><span>&nbsp;<a name=_Toc90810610>Layout</a></span></strong><span><strong><span>：<span>log</span>格式化器</span></strong></span><span>&nbsp;&nbsp;</span></h2>
<p><span>每个<span>appender</span>都和一个<span>layout</span>相联系；<span>layout</span>的任务是格式化用户的<span>logging request</span>。<span>Layout</span>使<span>log</span>的输出样式多样，比如可以在输出的<span>log</span>信息中包含<span>log</span>级别、<span>log</span>时间、线程、异常信息等。</span>&nbsp;</p>
<p><span>appender</span><span>的任务是把<span>layout</span>格式化好的输出内容送往指定的目的地。</span>&nbsp;</p>
<h2><span><span>3.5<span><strong>&nbsp;</strong></span></span><span><font size=3>&nbsp;</font></span></span><a name=_Toc90810611><span>Configuration</span></a><span><span>：配置信息</span></span><span>&nbsp;&nbsp;</span></h2>
<p><span>对<span>log4net</span>环境的配置就是对<span>Root Logger</span>的配置，包括把<span>Root Logger</span>设置为哪个级别<span>(level)</span>；为它增加哪些<span>appender</span>，等等。这些可以通过设置系统属性的方法来隐式地完成，也可以在程序里调用<span>XXXConfigurator.configure()</span>方法来显式地完成。</span><span>以上介绍的几部分是<span>log4net</span>中最主要的几部分，在使用<span>log4net</span>之前了解这几部分的主要结构和作用非常有助于更好的使用<span>log4net</span>。下面介绍<span>log4net</span>在<span>.net</span>应用程序中的使用方法。</span></p>
<p><a name=_Toc90810612><strong><span><font size=3>4 Log4net</font></span></strong></a><font size=3><span><strong><span>的使用方法</span></strong></span><strong><span>&nbsp;&nbsp;</span></strong></font> </p>
<p><span>在<span>.net</span>应用程序中使用<span>log4net</span>非常简单，完全不需要改变<span>.net</span>应用程序的结构，只需要为项目添加<span>log4net</span>引用，配置<span>config</span>文件，实例化一个<span>Logger</span>实例，在需要输出<span>log</span>的地方调用<span>Logger</span>的方法即可。使用<span>log4net</span>最重要的地方是<span>config</span>文件的配置。</span>&nbsp;</p>
<h2><strong><span><span>4.1&nbsp;</span></span></strong><strong><span>&nbsp;<a name=_Toc90810613>config</a></span></strong><span><strong><span>文件配置</span></strong></span><strong>&nbsp;</strong></h2>
<p><span>在<span>Application</span>项目中，<span>log4net</span>默认读取配置信息的文件是<span>App.config</span>，在<span>Web</span>项目中默认读取配置信息的文件是<span>Web.config</span>。但是<span>config</span>文件的配置方法在这两种情况下都是相同的，所以可以很简单的将一个<span>config</span>文件从<span>Application</span>项目移植到<span>Web</span>项目中。前面介绍过的将<span>log</span>输出到文本文件、数据库、<span>XML</span>文件等多种输出形式，以及<span>log</span>输出的多种样式等，在应用程序中只需要配置<span>config</span>文件即可实现，完全不用改动程序。下面介绍将<span>log</span>输出到文本文件和数据库的<span>config</span>文件配置方法。</span>&nbsp;</p>
<h3><a name=_Toc90810614></a><span><span>4.1.1</span></span><span>&nbsp;</span><span><span>输出<span>log</span>到文本文件的<span>config</span>文件配置</span></span><span>&nbsp;&nbsp;</span></h3>
<p><span>输出<span>log</span>到文本文件，需要在<span>config</span>文件中指定<span>log</span>文件的存放路径，文件名称，<span>log</span>输出格式，文本文件达到多大时重新创建文件等等，如下：</span>&nbsp;</p>
<pre><span>&lt;configuration&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;configSections&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&nbsp;&lt;section name="log4net" type="System.Configuration.IgnoreSectionHandler"</span></pre>
<pre><span>/&gt;</span>
<pre><span>&nbsp;&lt;/configSections&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;log4net&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;appender name="LogFileAppender" type="log4net.Appender.FileAppender"&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="File" value="c:\LogData\log.txt" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="AppendToFile" value="true" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&nbsp;&lt;layout type="log4net.Layout.PatternLayout"&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="Header" value="[Header]\r\n" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="Footer" value="[Footer]\r\n" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="ConversionPattern" value="%d [%t] %-5p %c [%x] </span></pre>
<pre><span>&amp;lt;%X{auth}&amp;gt; - %m%n" /&gt;</span>
<pre><span>&nbsp;&lt;/layout&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;/appender&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;root&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;level value="DEBUG" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;appender-ref ref="LogFileAppender" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&nbsp;&lt;/root&gt;&nbsp;&nbsp;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;logger name="OpenDB"&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span><span>&nbsp;&nbsp;&nbsp; </span>&lt;level value="INFO" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;/logger&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;/log4net&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;/configuration&gt;&nbsp;&nbsp;</span></pre>
<pre><span>下面分析<span>config</span>文件配置信息中主要部分代表的意义：</span></pre>
<p>&nbsp;</p>
<pre><span>&lt;configSections&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;section name="log4net" type="System.Configuration.IgnoreSectionHandler"/&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&lt;/configSections&gt;</span><span>
<p>&nbsp;</p>
</span></pre>
<p><span>这一部分定义了使用</span><span>System.Configuration.IgnoreSectionHandler</span><span>接口类读取<span>config</span>文</span></p>
<p><span>件中的配置信息，一般来说，这一部分在所有的应用中是相同的。</span></p>
<p>&nbsp;</p>
<p><span>&lt;</span><span>appender</span><span>&gt;</span><span>和<span>&lt;/appender&gt;</span>中间的部分分析：</span></p>
<p>&nbsp;</p>
<p><span>这一部分首先定义了<span>type="log4net.Appender.FileAppender"</span>是指要输出的<span>log</span>将写入到文件<span>(File)</span>中，如果是写入到数据库中，这里将这样配置：</span></p>
<p>&nbsp;</p>
<pre><span>&lt;appender name="ADOAppender" type="log4net.Appender.ADONetAppender"
<p>&nbsp;</p>
</span></pre>
<p><span>下面的部分指定了输出文件的路径及文件名</span><span>：</span></p>
<p>&nbsp;</p>
<pre><span>&lt;param name="File" value="c:\LogData\log.txt" /&gt;
<p>&nbsp;</p>
</span></pre>
<pre><span>&nbsp;&lt;param name="AppendToFile" value="true" /&gt;
<p>&nbsp;</p>
</span></pre>
<p><span>&lt;layout&gt;</span><span>和<span>&nbsp;&lt;/layout&gt;</span>中间的</span><span>部分指定<span>log</span>输出的样式：</span></p>
<p>&nbsp;</p>
&lt;PRE style="BACKGROUND: #dddddd; MARGIN: 12pt 0cm; LAYOUT-GRID-M</pre>
</pre>
<img src ="http://www.blogjava.net/superwei/aggbug/119191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-22 17:43 <a href="http://www.blogjava.net/superwei/articles/119191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB/VB.NET/C#导出到Excel的方法</title><link>http://www.blogjava.net/superwei/articles/118912.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Mon, 21 May 2007 08:11:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/118912.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/118912.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/118912.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/118912.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/118912.html</trackback:ping><description><![CDATA[先在VB的工程中引用&#8220;Microsoft Excel 11.0 Object &#8221;11.0是版本号，不重要的，Excel2000是9.0
<p>'申明变量</p>
<p>&nbsp;&nbsp;&nbsp; Dim xlApp As Excel.Application<br>&nbsp;&nbsp;&nbsp; Dim xlBook As Excel.Workbook<br>&nbsp;&nbsp;&nbsp; Dim xlSheet As Excel.Worksheet<br>&nbsp;&nbsp;&nbsp; Set xlApp = CreateObject("Excel.Application")</p>
<p>'使用模板<br>&nbsp;&nbsp;&nbsp; Set xlBook = xlApp.Workbooks.Open(App.Path &amp; "\普通.xls")<br>&nbsp;&nbsp;&nbsp; On Error GoTo 0<br>&nbsp;&nbsp;&nbsp; Set xlSheet = xlBook.Worksheets(1)<br>&nbsp;&nbsp;&nbsp; xlApp.Visible = False<br>&nbsp;&nbsp;&nbsp; xlSheet.Activate<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; '处理数据,填充Excel表<br>&nbsp;&nbsp;&nbsp; xlSheet.Cells(3, 4) = TextQuery.Text&nbsp;&nbsp; '产品名称</p>
<p>&nbsp;&nbsp;&nbsp; &#8230;&#8230;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; '//////////////<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; xlApp.Visible = True '显示表格<br>&nbsp;&nbsp;&nbsp; Set xlApp = Nothing '交还控制给Excel<br>&nbsp;&nbsp;&nbsp; Set xlBoook = Nothing<br>&nbsp;&nbsp;&nbsp; Set xlSheet = Nothing</p>
<p>到此，导出到Excel完成，在.NET中，只需改动少许即可运行，</p>
<img src ="http://www.blogjava.net/superwei/aggbug/118912.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-21 16:11 <a href="http://www.blogjava.net/superwei/articles/118912.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在vb.net中访问Excel(来自vb.net书) </title><link>http://www.blogjava.net/superwei/articles/118898.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Mon, 21 May 2007 07:48:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/118898.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/118898.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/118898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/118898.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/118898.html</trackback:ping><description><![CDATA[<p>本人使用的是Excel2000,须在项目里引用: 添加引用-&gt;Com-&gt;Microsoft Excel 9.0 Object Library </p>
<p>1 打开Excel dim myexcel as new Excel.Application() myexcel.visible=true </p>
<p>2 添加新的工作簿 myexcel.Workbooks.add() </p>
<p>3 设定第二个工作表为活动工作表 myexcel.worksheets(2).Acivate() </p>
<p>4 打开指定的Excel文件 myexcel.workbooks.open("c:\my.xls") </p>
<p>5 显示Excel窗口 myexcel.visible=true </p>
<p>6 更改Excel的标题栏 myexcel.caption="欢迎,欢迎!" </p>
<p>7 为Excel的单元格赋值 myexcel.cells(1,4).value=100 此语句使Excel当前工作表的第一行第四列,即D1单元格等于100, 也可以这样写: myexcel.Range("D1").value=100 </p>
<p>8 设置指定列的宽度(单位:字符个数) myexcel.ActiveSheet.colums(1).columnwidth=20 设定当前工作表第1列的宽度为20</p>
<p>&nbsp;9 设置指定行的高度(单位:磅) myexcel.ActiveSheet.rows(1).rowHeight=1/0.035 1磅 =0.035厘米 设置第1行的高度为1CM </p>
<p>10 插入分页符 myexcel.Activesheet.rows(20).pagebreak=1 在第20行前插入分页符 </p>
<p>11 删除分页符 myexcel.Activesheet.columns(20).pagebreak=0 在第20列前删除分页符 </p>
<p>12 指定边框线的宽度 myexcel.Activesheet.range("B3:D3").borders(1).weight=3 其中borders参数指定单元格边框的位置: 1:左 2:右 3:顶 4:底 5:斜\ 6:斜/ </p>
<p>13 指定边框线条的类型 myexcel.Activesheet.range("B1:D3").borders(2).linestyle=1 此语句将当前工作表的B1:D3单元格的右边框设置为实线 linestyle参数: 1:细实线 2:细虚线 3:点虚线 4:双细实线 </p>
<p>14 设置页脚 myexcel.activesheet.pagesetup.centerfooter="第&amp;p页" 注意:设置页眉页脚时要保证计算机上装有打印机,否则出错! </p>
<p>15 设置页眉 myexcel.activesheet.pagesetup.centerfooter="第&amp;p页" </p>
<p>16 设置页眉到顶断距离为2cm myexcel.Activesheet.pagesetup.Headermargin=2/0.035 </p>
<p>17 设置页脚到底端距离为2cm myexcel.Activesheet.pagesetup.Footermargin=2/0.035 </p>
<p>18 设置顶边边距为2cm myexcel.Activesheet.pagesetup.topmargin=2/0.035 </p>
<p>19 设置底边边距为2cm myexcel.Activesheet.pagesetup.Bottommargin=2/0.035 </p>
<p>20 设置左边边距为2cm myexcel.Activesheet.pagesetup.Leftmargin=2/0.035 </p>
<p>21 设置右边边距为2cm myexcel.Activesheet.pagesetup.Rightmargin=2/0.035</p>
<p>22&nbsp; 设置页面水平居中 myexcel.activesheet.pagesetup.CenterHorizontally=true</p>
<p>23&nbsp; 设置页面垂直居中 myexcel.activesheet.pagesetup.Centervertically=true</p>
<p>24 设置页面纸张大小 (1,窄行 8.5*11&nbsp; ;39 ,宽行 14*11)&nbsp; myexcel.activesheet.pagesetup.papersize=1</p>
<p>25&nbsp; 打印单元格网格线&nbsp; myexcel.activesheet.pagesetup.PrintGridlines=true</p>
<p>26&nbsp; 复制整个工作表&nbsp; myexcel.activesheet.Usedrange.Copy</p>
<p>27 复制指定区域&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myexcel.activesheet.range("a1:b5").Copy</p>
<p>28 粘贴&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myexcel.worksheets("sheet2").range("A1").PasteSpecial</p>
<p>29 在第2行前插入一行&nbsp;&nbsp;&nbsp; myexcel.activesheet.rows(2).Insert</p>
<p>30&nbsp; 在第2列前插入一列&nbsp;&nbsp; myexcel.Activesheet.Columns(2).Insert</p>
<p>31 合并 C4:D4 单元格&nbsp; myexcel.Activesheet.Range("C4:D4").Merge()</p>
<p>32 自动调整第2列列宽&nbsp; myexcel.activesheet.Columns(2).AutoFit</p>
<p>33 设置字体 myexcel.Activesheet.cells(2,1).font.name="黑体"</p>
<p>34 设置字体大小 myexcel.Activesheet.cells(2,1).font.size=25</p>
<p>35 设置字体为斜体&nbsp; myexcel.Activesheet.cells(2,1).font.Italic=true</p>
<p>36 设置字体为粗体&nbsp; myexcel.Activesheet.cells(2,1).font.Bold=true</p>
<p>37 清除单元格内容 myexcel.activesheet.cells(2,1).ClearContents</p>
<p>38 打印预览工作表 myexcel.Activesheet.PrintPreview</p>
<p>39 打印工作表&nbsp; myexcel.Activesheet.Printout</p>
<p>40 工作表另存为 myexcel.ActiveWorkbook.saveas("C:\book2.xls")</p>
<p>41 放弃存盘&nbsp; myexcel.ActiveWorkbook.saved=false</p>
<p>42 关闭工作簿&nbsp; myexcel.Workbooks.close</p>
<p>43 退出 Excel&nbsp;&nbsp; myexcel.quit</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/superwei/aggbug/118898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-21 15:48 <a href="http://www.blogjava.net/superwei/articles/118898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Microsoft .Net Remoting系列专题之三：Remoting事件处理全接触（转载）</title><link>http://www.blogjava.net/superwei/articles/115911.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 08 May 2007 05:09:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/115911.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/115911.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/115911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/115911.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/115911.html</trackback:ping><description><![CDATA[<p>前言：在Remoting中处理事件其实并不复杂，但其中有些技巧需要你去挖掘出来。正是这些技巧，仿佛森严的壁垒，让许多人望而生畏，或者是不知所谓，最后放弃了事件在Remoting的使用。关于这个主题，在网上也有很多讨论，相关的技术文章也不少，遗憾的是，很多文章概述的都不太全面。我在研究Remoting的时候，也对事件处理发生了兴趣。经过参考相关的书籍、文档，并经过反复的试验，深信自己能够把这个问题阐述清楚了。<br>本文对于Remoting和事件的基础知识不再介绍，有兴趣的可以看我的系列文章，或查阅相关的技术文档。</p>
<p>本文示例代码下载：</p>
<p><a href="http://www.cnblogs.com/Files/wayfarer/Remoting事件(客户端发传真).rar"><font color=#0066aa>Remoting事件(客户端发传真)</font></a></p>
<p><a href="http://www.cnblogs.com/Files/wayfarer/Remoting事件(服务端广播).rar"><font color=#0066aa>Remoting事件(服务端广播)</font></a></p>
<p><a href="http://www.cnblogs.com/Files/wayfarer/Remoting事件(服务端广播改进).rar"><font color=#0066aa>Remoting事件(服务端广播改进)</font></a></p>
<p>应用Remoting技术的分布式处理程序，通常包括三部分：远程对象、服务端、客户端。因此从事件的方向上看，就应该有三种形式：<br>1、服务端订阅客户端事件<br>2、客户端订阅服务端事件<br>3、客户端订阅客户端事件</p>
<p>服务端订阅客户端事件，即由客户端发送消息，服务端捕捉该消息，然后响应该事件，相当于下级向上级发传真。反过来，客户端订阅服务端事件，则是由服务端发送消息，此时，所有客户端均捕获该消息，激发事件，相当于是一个系统广播。而客户端订阅客户端事件呢？就类似于聊天了。由某个客户端发出消息，其他客户端捕获该消息，激发事件。可惜的是，我并没有找到私聊的解决办法。当客户端发出消息后，只要订阅了该事件的，都会获得该信息。</p>
<p>然而不管是哪一种方式，究其实质，真正包含事件的还是远程对象。原理很简单，我们想一想，在Remoting中，客户端和服务端传递的内容是什么呢？毋庸置疑，是远程对象。因此，我们传递的事件消息，自然是被远程对象所包裹。这就像EMS快递，远程对象是运送信件的汽车，而事件消息就是汽车所装载的信件。至于事件传递的方向，只是发送者和订阅者的角色发生了改变而已。</p>
<p><strong>一、&nbsp;服务端订阅客户端事件</strong><br>服务端订阅客户端事件，相对比较简单。我们就以发传真为例。首先，我们必须具备传真机和要传真的文件，这就好比我们的远程对象。而且这个传真机上必须具备&#8220;发送&#8221;的操作按钮。这就好比是远程对象中的一个委托。当客户发送传真时，就需要在客户端上激活一个发送消息的方法，这就好比我们按了&#8220;发送&#8221;按钮。消息发送到服务端后，触发事件，这个事件正是服务端订阅的。服务端获得该事件消息后，再处理相关业务。这就好比接收传真的人员，当传真收到后，会听到接通的声音，此时选择&#8220;接收&#8221;后，该消息就被捕获了。</p>
<p>现在，我们就来模拟这个流程。首先定义远程对象，这个对象处理的应该是一个发送传真的业务：<br>首先是远程对象的公共接口（Common.dll）：<br>public delegate void FaxEventHandler(string fax);<br>public interface IFaxBusiness<br>{<br>&nbsp;&nbsp;&nbsp; void SendFax(string fax);<br>}<br>注意，在公共接口程序集中，定义了一个公共委托。</p>
<p>然后我们定义具体处理传真业务的远程对象类（FaxBusiness.dll），在这个类中，先要添加对公共接口程序集的引用：<br>public class FaxBusiness:MarshalByRefObject,IFaxBusiness<br>{&nbsp;<br>&nbsp;public static event FaxEventHandler FaxSendedEvent;</p>
<p>&nbsp;#region</p>
<p>&nbsp;public void SendFax(string fax)<br>&nbsp;{<br>&nbsp;&nbsp;if (FaxSendedEvent != null)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;FaxSendedEvent(fax);<br>&nbsp;&nbsp;}<br>&nbsp;}</p>
<p>&nbsp;#endregion</p>
<p>&nbsp;public override object InitializeLifetimeService()<br>&nbsp;{<br>&nbsp;&nbsp;return null;<br>&nbsp;}<br>}<br>这个远程对象中，事件的类型就是我们在公共程序集Common.dll中定义的委托类型。SendFax实现了接口IFaxBusiness中的方法。这个方法的签名和定义的委托一致，它调用了事件FaxSendedEvent。<br>特殊的地方是我们定义的远程对象最好是重写MarshalByRefObject类的InitializeLifetimeService()方法。返回null值表明这个远程对象的生命周期为无限大。为什么要重写该方法呢？道理不言自明，如果生命周期不进行限制的话，一旦远程对象的生命周期结束，事件就无法激活了。<br>接下来就是分别实现客户端和服务端了。服务端是一个Windows应用程序，界面如下：</p>
<p><img height=300 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re01.gif" width=300 border=0><br>&nbsp;<br>我们在加载窗体的时候，注册通道和远程对象：<br>private void ServerForm_Load(object sender, System.EventArgs e)<br>{<br>&nbsp;HttpChannel channel = new HttpChannel(8080);<br>&nbsp;ChannelServices.RegisterChannel(channel);</p>
<p>&nbsp;RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;typeof(FaxBusiness),"FaxBusiness.soap",WellKnownObjectMode.Singleton);<br>&nbsp;FaxBusiness.FaxSendedEvent += new FaxEventHandler(OnFaxSended);<br>}</p>
<p>我们采用的是SingleTon模式，注册了一个远程对象。注意看，这段代码和一般的Remoting服务端有什么区别？对了，它多了一行注册事件的代码：<br>FaxBusiness.FaxSendedEvent += new FaxEventHandler(OnFaxSended);<br>这行代码，就好比我们服务端的传真机，一直切换为&#8220;自动&#8221;模式。它会一直监听着来自客户端的传真信息，一旦传真信息从客户端发过来了，则响应事件方法，即OnFaxSended方法：<br>public void OnFaxSended(string fax)<br>{<br>&nbsp;txtFax.Text += fax;<br>&nbsp;txtFax.Text += System.Environment.NewLine;<br>}<br>这个方法很简单，就是把客户端发过来的Fax显示到txtFax文本框控件上。</p>
<p>而客户端呢？仍然是一个Windows应用程序。代码非常简单，首先为了简便其见，我们仍然让它在装载窗体的时候，激活远程对象：<br>private void ClientForm_Load(object sender, System.EventArgs e)<br>{<br>&nbsp;HttpChannel channel = new HttpChannel(0);<br>&nbsp;ChannelServices.RegisterChannel(channel);</p>
<p>&nbsp;faxBus = (IFaxBusiness)Activator.GetObject(typeof(IFaxBusiness),<br>&nbsp;&nbsp;"<a href="http://localhost:8080/FaxBusiness.soap"><font color=#0066aa>http://localhost:8080/FaxBusiness.soap</font></a>");<br>}<br>呵呵，可以说客户端激活对象的方法和普通的Remoting客户端应用程序没有什么不同。该写传真了！我们在窗体上放一个文本框对象，改其Multiline属性为true。再放一个按钮，负责发送传真：<br>private void btnSend_Click(object sender, System.EventArgs e)<br>{<br>&nbsp;if (txtFax.Text != String.Empty)<br>&nbsp;{<br>&nbsp;&nbsp;string fax = "来自" + GetIpAddress() + "客户端的传真:" <br>+ System.Environment.NewLine;<br>&nbsp;&nbsp;fax += txtFax.Text;<br>&nbsp;&nbsp;faxBus.SendFax(fax);<br>&nbsp;}<br>&nbsp;else<br>&nbsp;{<br>&nbsp;&nbsp;MessageBox.Show("请输入传真内容!");<br>&nbsp;}<br>}</p>
<p>private string GetIpAddress()<br>{&nbsp;&nbsp;&nbsp;<br>&nbsp;IPHostEntry ipHE = Dns.GetHostByName(Dns.GetHostName());<br>&nbsp;return ipHE.AddressList[0].ToString();&nbsp;&nbsp;&nbsp;<br>}</p>
<p>在这个按钮单击事件中，只需要调用远程对象faxBus的SendFax()方法就OK了，非常简单。可是慢着，为什么你的代码有这么多行啊？其实，没有什么奇怪的，我只是想到发传真的客户可能会很多。为了避免服务端人员犯糊涂，搞不清楚是谁发的，所以要求在传真上加上各自的签名，也就是客户端的IP地址了。既然要获得计算机的IP地址，请一定要记得加上对DNS的命名空间引用：<br>using System.Net;</p>
<p>因为我们严格按照分布式处理程序的部署方式，所以在客户端只需要添加公共程序集(Common.dll)的引用就可以了。而在服务端呢，则必须添加公共程序集和远程对象程序集两者的引用。</p>
<p>OK，程序完成，我们来看看这个简陋的传真机：<br>客户端：</p>
<p><img height=300 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re02.gif" width=300 border=0><br>&nbsp;<br>嘿嘿，做梦都想放假啊。好的，传真写好了，发送吧！再看看服务端，great，老板已经收到我的请假条传真了！</p>
<p><img height=300 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re03.gif" width=300 border=0><br>&nbsp;</p>
<p><strong>二、&nbsp;客户端订阅服务端事件</strong></p>
<p>嘿嘿，吃甘蔗要先吃甜的一段，做事情我也喜欢先做容易的。现在，好日子过去了，该吃点苦头了。我们先回忆一下刚才的实现方法，再来思考怎么实现客户端订阅服务端事件？</p>
<p>在前一节，事件被放到远程对象中，客户端激活对象后，就可以发送消息了。而在服务端，只需要订阅该事件就可以。现在思路应该反过来，由客户端订阅事件，服务端发送消息。就这么简单吗？先不要高兴得太早。我们想一想，发送消息的任务是谁来完成的？是远程对象。而远程对象是什么时候创建的呢？我们仔细思考Remoting的几种激活方式，不管是服务端激活，还是客户端激活，他们的工作原理都是：客户端决定了服务器创建远程对象实例的时机，例如调用了远程对象的方法。而服务端所作的工作则是注册该远程对象。</p>
<p>回忆这三种激活方式在服务端的代码：<br>SingleCall激活方式：<br>RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;typeof(BroadCastObj),"BroadCastMessage.soap",<br>&nbsp;&nbsp;WellKnownObjectMode.Singlecall);<br>SingleTon激活方式：<br>RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;typeof(BroadCastObj),"BroadCastMessage.soap",<br>&nbsp;&nbsp;WellKnownObjectMode.Singleton);<br>客户端激活方式：<br>RemotingConfiguration.ApplicationName = &#8220;BroadCastMessage.soap&#8221;<br>RemotingConfiguration.RegisterActivatedServiceType(typeof(BroadCastObj));</p>
<p>请注意Register这个词语，它表达的含义就是注册。也就是说，在服务端并没有显示的创建远程对象实例。没有该实例，又如何广播消息呢？</p>
<p>或许有人会想，在注册远程对象之后，显式实例该对象不就可以了吗？也就是说，在注册后加上这一段代码：<br>BroadCastObj obj = new BroadCastObj();</p>
<p>然而，我们要明白一个事实：就是服务端和客户端是处于两个不同的应用程序域中。因此在Remoting中，客户端获得的远程对象实际是服务端注册对象的代理。如果我们在注册后，人工去创建一个实例，而非Remoting在激活后自动创建的对象，那么客户端获得的对象与服务端人工创建的实例是两个迥然不同的对象。客户端获得的代理对象并没有指向你刚才创建的obj实例。所以obj发送的消息，客户端根本无法捕捉。</p>
<p>那么，我们只有望洋兴叹，束手无策了吗？别着急，别忘了在服务器注册对象方法中，还有一种方法，即Marshal方法啊。还记得Marshal的实现方式吗？<br>BroadCastObj Obj = new BroadCastObj();<br>ObjRef objRef = RemotingServices.Marshal(Obj,"BroadCastMessage.soap");</p>
<p>这个方法与前不一样。前面的三种方式，远程对象是根据客户端调用的方式，来自动创建的。而Marshal方法呢？则显式地创建了远程对象实例，然后将其Marshal到通道中，形成ObjRef指向对象的代理。只要生命周期没有结束，这个对象就一直存在。而此时客户端获得的对象，正是创建的Obj实例的代理。</p>
<p>OK，这个问题解决了，我们来看看具体实现。<br>公共程序集和远程对象与前相似，就不再赘述，只附上代码：<br>公共程序集：<br>public delegate void BroadCastEventHandler(string info);&nbsp;</p>
<p>public interface IBroadCast<br>{<br>&nbsp;event BroadCastEventHandler BroadCastEvent;<br>&nbsp;void BroadCastingInfo(string info);<br>}<br>远程对象类：<br>public event BroadCastEventHandler BroadCastEvent;</p>
<p>#region IBroadCast 成员</p>
<p>//[OneWay]<br>public void BroadCastingInfo(string info)<br>{<br>&nbsp;if (BroadCastEvent != null)<br>&nbsp;{<br>&nbsp;&nbsp;BroadCastEvent(info);<br>&nbsp;}<br>}</p>
<p>#endregion</p>
<p>public override object InitializeLifetimeService()<br>{<br>&nbsp;return null;<br>}</p>
<p>下面，该实现服务端了。在实现之前，我还想罗嗦几句。在第一节中，我们实现了服务端订阅客户端事件。由于订阅事件是在服务端发生的，因此事件本身并未被传送。被序列化的仅仅是传递的消息，即Fax而已。现在，方向发生了改变，传送消息的是服务端，客户端订阅了事件。但这个事件是放在远程对象中的，因此事件必须被序列化。而在.Net Framework1.1中，微软对序列化的安全级别进行了限制。有关委托和事件的序列化、反序列化默认是禁止的，所以我们应该将TypeFilterLevel的属性值设置为Full枚举值。因此在服务端注册通道的方式就发生了改变：<br>private void StartServer()<br>{<br>&nbsp;BinaryServerFormatterSinkProvider serverProvider = new <br>&nbsp;&nbsp;BinaryServerFormatterSinkProvider();<br>&nbsp;BinaryClientFormatterSinkProvider clientProvider = new <br>&nbsp;&nbsp;BinaryClientFormatterSinkProvider();<br>&nbsp;serverProvider.TypeFilterLevel = TypeFilterLevel.Full;</p>
<p>&nbsp;IDictionary props = new Hashtable();<br>&nbsp;props["port"] = 8080;<br>&nbsp;&nbsp;&nbsp; HttpChannel channel = new HttpChannel(props,clientProvider,serverProvider);<br>&nbsp;ChannelServices.RegisterChannel(channel);</p>
<p>&nbsp;Obj = new BroadCastObj();<br>&nbsp;ObjRef objRef = RemotingServices.Marshal(Obj,"BroadCastMessage.soap");&nbsp;<br>}</p>
<p>注意语句serverProvider.TypeFilterLevel = TypeFilterLevel.Full；此语句即设置序列化安全级别的。要使用TypeFilterLevel属性，必须申明命名空间：<br>using System.Runtime.Serialization.Formatters;</p>
<p>而后面两条语句就是注册远程对象。由于在我的广播程序中，发送广播消息是放在另一个窗口中，因此我将该远程对象声明为公共静态对象：<br>public static BroadCastObj Obj = null;</p>
<p>然后在调用窗口事件中加入：<br>private void ServerForm_Load(object sender, System.EventArgs e)<br>{<br>&nbsp;StartServer();<br>&nbsp;lbMonitor.Items.Add("Server started!");<br>}<br>来看看界面，首先启动服务端主窗口：</p>
<p><img height=312 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re04.gif" width=304 border=0><br>&nbsp;<br>我放了一个ListBox控件来显示一些信息，例如显示服务器启动了。而BroadCast按钮就是广播消息的，单击该按钮，会弹出一个对话框：</p>
<p><img height=176 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re05.gif" width=320 border=0><br>&nbsp;<br>BraodCast按钮的代码：<br>private void btnBC_Click(object sender, System.EventArgs e)<br>{&nbsp;&nbsp;&nbsp;<br>&nbsp;BroadCastForm bcForm = new BroadCastForm();<br>&nbsp;bcForm.StartPosition = FormStartPosition.CenterParent;<br>&nbsp;bcForm.ShowDialog();<br>}</p>
<p>在对话框中，最主要的就是Send按钮：<br>if (txtInfo.Text != string.Empty)<br>{ &nbsp;<br>&nbsp;ServerForm.Obj.BroadCastingInfo(txtInfo.Text);<br>}<br>else<br>{<br>&nbsp;MessageBox.Show("请输入信息！");<br>}<br>但是很简单，就是调用远程对象的发送消息方法而已。</p>
<p>现在该实现客户端了。我们可以参照前面的例子，只是把服务端改为客户端而已。另外考虑到序列化安全级别的问题，所以代码会是这样：<br>private void ClientForm_Load(object sender, System.EventArgs e)<br>{<br>&nbsp;BinaryServerFormatterSinkProvider serverProvider = new <br>&nbsp;&nbsp;BinaryServerFormatterSinkProvider();<br>&nbsp;BinaryClientFormatterSinkProvider clientProvider = new <br>&nbsp;&nbsp;BinaryClientFormatterSinkProvider();<br>&nbsp;serverProvider.TypeFilterLevel = TypeFilterLevel.Full;</p>
<p>&nbsp;IDictionary props = new Hashtable();<br>&nbsp;props["port"] = 0;<br>&nbsp;HttpChannel channel = new HttpChannel(props,clientProvider,serverProvider);<br>&nbsp;ChannelServices.RegisterChannel(channel);</p>
<p>&nbsp;watch = (IBroadCast)Activator.GetObject(<br>&nbsp;&nbsp;typeof(IBroadCast),"<a href="http://localhost:8080/BroadCastMessage.soap"><font color=#0066aa>http://localhost:8080/BroadCastMessage.soap</font></a>");&nbsp;<br>&nbsp;watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);<br>}<br>注意客户端通道的端口号应设置为0，这表示客户端自动选择可用的端口号。如果要设置为指定的端口号，则必须保证与服务端通道的端口号不相同。<br>然后是，BroadCastEventHandler委托的方法：<br>public void BroadCastingMessage(string message)<br>{<br>&nbsp;txtMessage.Text += "I got it:" + message;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;txtMessage.Text += System.Environment.NewLine;&nbsp;&nbsp;&nbsp;<br>}<br>客户端界面如图：</p>
<p><img height=312 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re06.gif" width=344 border=0><br>&nbsp;<br>好，下面让我们满怀期盼，来运行这段程序。首先启动服务端应用程序，然后启动客户端。哎呀，糟糕，居然出现了错误信息！</p>
<p><img height=166 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re07.gif" width=437 border=0><br>&nbsp;</p>
<p>&#8220;人之不如意事，十常居八九。&#8221;不用沮丧，让我们分析原因。首先看看错误信息，它报告我们没有找到Client程序集。然而事实上，Client程序集当然是有的。那么再来调试一下，是哪一步出现的问题呢？设置好断点，进行逐语句跟踪。前面注册通道一切正常，当运行到watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage)语句时，错误出现了！</p>
<p>也就是说，远程对象的创建是成功的，但在订阅事件的时候失败了。原因是什么呢？原来，客户端的委托是通过序列化后获得的，在订阅事件的时候，委托试图装载包含与签名相同的方法的程序集，也就是BroadCastingMessage方法所在的程序集Client。然而这个装载的过程发生在服务端，而在服务端，并没有Client程序集存在，自然就发生了上面的异常。</p>
<p>原因清楚了，怎么解决？首先BroadCastingMessage方法肯定是在客户端中，所以不可避免，委托装载Client程序集的过程也必须在客户端完成。而服务端事件又是由远程对象来捕获的，因此，在客户端注册的也就必须是远程对象事件了。一个要求必须在客户端，一个又要求必须在服务端，事情出现了自相矛盾的地方。</p>
<p>那么，让我们先想想这样一个例子。假设我们要交换x和y的值，该这样完成？很简单，引入一个中间变量就可以了。<br>int x=1,y=2,z;<br>z = x;<br>x = y;<br>y = z;<br>这个游戏相信大家都会玩吧，那么好的，我们也需要引入这样一个&#8220;中间&#8221;对象。这个中间对象和原来的远程对象在事件处理方面，代码完全一致：<br>public class EventWrapper:MarshalByRefObject<br>{<br>&nbsp;public event BroadCastEventHandler LocalBroadCastEvent;</p>
<p>&nbsp;//[OneWay]<br>&nbsp;public void BroadCasting(string message)<br>&nbsp;{<br>&nbsp;&nbsp;LocalBroadCastEvent(message);<br>&nbsp;}</p>
<p>&nbsp;public override object InitializeLifetimeService()<br>&nbsp;{<br>&nbsp;&nbsp;return null;<br>&nbsp;}<br>}</p>
<p>不过不同之处在于：这个Wrapper类必须在客户端和服务端上都要部署，所以，这个类应该放在公共程序集Common.dll中。</p>
<p>现在再来修改原来的客户端代码：<br>watch = (IBroadCast)Activator.GetObject(<br>&nbsp;&nbsp;typeof(IBroadCast),"<a href="http://localhost:8080/BroadCastMessage.soap"><font color=#0066aa>http://localhost:8080/BroadCastMessage.soap</font></a>");&nbsp;<br>watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);<br>修改为：<br>watch = (IBroadCast)Activator.GetObject(<br>&nbsp;&nbsp;&nbsp;&nbsp;typeof(IBroadCast),"<a href="http://localhost:8080/BroadCastMessage.soap"><font color=#0066aa>http://localhost:8080/BroadCastMessage.soap</font></a>");<br>EventWrapper wrapper = new EventWrapper();&nbsp;<br>wrapper.LocalBroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);<br>watch.BroadCastEvent += new BroadCastEventHandler(wrapper.BroadCasting);</p>
<p>为什么这样做就可以了呢？也许画一幅图就很容易说明，可惜我的艺术天分实在很糟糕，我希望以后可以改进这一点。还是用文字来说明吧。</p>
<p>前面说，委托要装载client程序集。现在我们把远程对象委托装载的权利移交给EventWrapper。因为这个类对象是放在客户端的，所以它要装载client程序集丝毫没有问题。语句：<br>EventWrapper wrapper = new EventWrapper();&nbsp;<br>wrapper.LocalBroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);<br>实现了这个功能。</p>
<p>不过此时虽然订阅了事件，但事件还是客户端的，没有与服务端联系起来。而服务端的事件是放到远程对象中的，所以，还要订阅事件，这个任务由远程对象watch来完成。但此时它订阅的不再是BroadCastingMessage了，而是EventWrapper的触发事件方法BroadCasting。那么此时委托同样要装载程序集，但此时装载的就是BroadCasting所在的程序集了。由于装载发生的地点是在服务端。呵呵，高兴的是，BroadCasting所在的程序集正是公共程序集（前面已说过，EventWrapper应放到公共程序集Common.dll中），而公共程序集在服务端和客户端都已经部署了。自然就不会出现找不到程序集的问题了。</p>
<p>注意：EventWrapper因为要重写InitializeLifetimeService()方法，所以仍然要继承MarshalByRefObject类。</p>
<p>现在再来运行程序。首先运行服务端；然后运行客户端，OK，客户端窗体出现了：</p>
<p><img height=312 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re06.gif" width=344 border=0><br>&nbsp;<br>然后我们在服务端单击&#8220;BroadCast&#8221;按钮，发送广播消息：</p>
<p><img height=176 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re08.gif" width=320 border=0><br>&nbsp;<br>单击&#8220;Send&#8221;发送，再来看看客户端，会是怎样？Fine，I got it!</p>
<p><img height=312 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re09.gif" width=344 border=0><br>&nbsp;<br>怎么样，很酷吧！你也可以同时打开多个客户端，它们都将收到这个广播信息。如果你觉得这个广播声音太吵，那就请你在客户端取消广播吧。在Cancle按钮中：<br>private void btnCancle_Click(object sender, System.EventArgs e)<br>{<br>&nbsp;watch.BroadCastEvent -= new BroadCastEventHandler(wrapper.BroadCasting);<br>&nbsp;MessageBox.Show("取消订阅广播成功!");<br>}<br>当然这个时候wrapper对象应该被申明为private对象了：<br>private EventWrapper wrapper = null;</p>
<p><img height=100 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re10.gif" width=128 border=0><br>&nbsp;<br>取消后，你试着再广播一下，恭喜你，你不会听到噪音了！</p>
<p><strong>三、&nbsp;客户端订阅客户端事件<br></strong><br>有了前面的基础，再来看客户端订阅客户端事件，就简单多了。而本文写到这里，我也很累了，你也被我啰嗦得不耐烦了。你心里在喊，&#8220;饶了我吧！&#8221;其实，我又何尝不是如此。所以我只提供一个思路，有兴趣的朋友，可以自己写一个程序。</p>
<p>其实方法很简单，和第二种情况类似。发送信息的客户端，只需要获得远程对象后，发送消息就可以了。而接收信息的客户端，负责订阅该事件。由于事件都是放到远程对象中，因此订阅的方法和第二种情况没有什么区别！</p>
<p>特殊的情况是，我们可以用第三种情况来代替第二种。只要你把发送信息的客户端放到服务端就可以了。当然需要做一些额外的工作，有兴趣的朋友可以去实现一下。在我的示例程序中，已经用这种方法模拟实现了服务端的广播，大家可以去看看。</p>
<p><strong>四、&nbsp;一点补充<br></strong><br>我在前面的事件处理中，使用的都是默认的EventArgs。如果要定义自己的EventArgs，就不相同了。因为该信息是传值序列化，因此必须加上[Serializable]，且必须放到公共程序集中，部署到服务端和客户端。例如：<br>[Serializable]<br>public class BroadcastEventArgs:EventArgs<br>{<br>&nbsp;private string msg = null;<br>&nbsp;public BroadcastEventArgs(string message)<br>&nbsp;{<br>&nbsp;&nbsp;msg = message;<br>&nbsp;}</p>
<p>&nbsp;public string Message<br>&nbsp;{<br>&nbsp;&nbsp;get {return msg;}<br>&nbsp;}<br>}</p>
<p><strong>五、持续改进（经Beta的提醒，我改进了我的程序，并对文章进行了修改 2004年12月13日）</strong></p>
<p>也许，细心的读者注意到了，在我的远程对象类和EventWrapper类中，触发事件方法的Attribute[OneWay]被我注释掉了。我看到很多资料上写到，在Remoting中处理事件，触发事件的方法必须具有这个Attribute。这个attribute究竟有什么用？</p>
<p>在发送事件消息的时候，事件的订阅者会触发事件，然后响应该事件。然而当事件的订阅者发生错误的时候呢？例如，发送事件消息的时候，才发现根本没有事件订阅者；或者事件的订阅者出现故障，如断电、或异常关机。此时，发送事件一方会因为找不到正确的事件订阅者，而发生异常。以我的程序为例。当我们分别打开服务端和客户端程序的时候，此时广播信息正常。然而，当我们关闭客户端后，由于该客户端没有取消订阅，此时异常发生，提示信息如图：</p>
<p><img height=152 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re11.gif" width=437 border=0></p>
<p>（不知道为什么，这个异常与客户端连接服务端出现的异常一样。这个异常容易让人产生误会。）</p>
<p>如果这个时候我们同时打开了多个客户端，那么其他客户端就会因为这一个客户端关闭造成的错误，而无法收到广播信息。那么让我们先做第一步改进：</p>
<p>1、先考虑正常情况。在我的客户端，虽然提供了取消订阅的操作，但并没有考虑用户关闭客户端的情况。即，关闭客户端时，并未取消事件的订阅，所以我们应该在关闭客户端窗体中写入：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ClientForm_Closing(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">&nbsp;sender,&nbsp;System.ComponentModel.CancelEventArgs&nbsp;e)<br><img id=Codehighlighter1_92_172_Open_Image onclick="this.style.display='none'; Codehighlighter1_92_172_Open_Text.style.display='none'; Codehighlighter1_92_172_Closed_Image.style.display='inline'; Codehighlighter1_92_172_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_92_172_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_92_172_Closed_Text.style.display='none'; Codehighlighter1_92_172_Open_Image.style.display='inline'; Codehighlighter1_92_172_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_92_172_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_92_172_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;watch.BroadCastEvent&nbsp;</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;BroadCastEventHandler(wrapper.BroadCasting);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span></div>
</div>
<p>2、仅仅是这样还不够。如果客户端并没有正常关闭，而是因为突然断电而导致客户端关闭呢？此时，客户端还没有来得及取消事件订阅呢。在这种情况下，我们需要用到OneWayAttribute。</p>
<p>前面说到，发送事件一方如果找不到正确的事件订阅者，会发生异常。也就是说，这个事件是unreachable的。幸运的是，OneWayAttribute恰好解决了这个问题。其实从该特性的命名OneWay，大约也能猜到其中的含义。当事件不可到达，无法发送时，正常情况下，会返回一个异常信息。如果加上OneWayAttribute，这个事件的发送就变成单向的了。假如此时发生异常，那么系统会自动抛掉该异常信息。由于没有异常信息的返回，发送信息方会认为发送信息成功了。程序会正常运行，错误的客户端被忽略，而正确的客户端仍然能够收到广播信息。</p>
<p>因此，远程对象的代码就应该是这样：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;BroadCastEventHandler&nbsp;BroadCastEvent;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><br><img id=Codehighlighter1_52_201_Closed_Image onclick="this.style.display='none'; Codehighlighter1_52_201_Closed_Text.style.display='none'; Codehighlighter1_52_201_Open_Image.style.display='inline'; Codehighlighter1_52_201_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><img id=Codehighlighter1_52_201_Open_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_52_201_Open_Text.style.display='none'; Codehighlighter1_52_201_Closed_Image.style.display='inline'; Codehighlighter1_52_201_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top></span><span id=Codehighlighter1_52_201_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">IBroadCast&nbsp;成员</span><span id=Codehighlighter1_52_201_Open_Text style="DISPLAY: none"><span style="COLOR: #0000ff">#region</span><span style="COLOR: #000000">&nbsp;IBroadCast&nbsp;成员</span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[OneWay]</span><span style="COLOR: #008000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;BroadCastingInfo(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;info)<br><img id=Codehighlighter1_128_189_Open_Image onclick="this.style.display='none'; Codehighlighter1_128_189_Open_Text.style.display='none'; Codehighlighter1_128_189_Closed_Image.style.display='inline'; Codehighlighter1_128_189_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_128_189_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_128_189_Closed_Text.style.display='none'; Codehighlighter1_128_189_Open_Image.style.display='inline'; Codehighlighter1_128_189_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_128_189_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_128_189_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(BroadCastEvent&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">)<br><img id=Codehighlighter1_160_187_Open_Image onclick="this.style.display='none'; Codehighlighter1_160_187_Open_Text.style.display='none'; Codehighlighter1_160_187_Closed_Image.style.display='inline'; Codehighlighter1_160_187_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_160_187_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_160_187_Closed_Text.style.display='none'; Codehighlighter1_160_187_Open_Image.style.display='inline'; Codehighlighter1_160_187_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_160_187_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_160_187_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;BroadCastEvent(info);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #0000ff">#endregion</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">&nbsp;InitializeLifetimeService()<br><img id=Codehighlighter1_255_271_Open_Image onclick="this.style.display='none'; Codehighlighter1_255_271_Open_Text.style.display='none'; Codehighlighter1_255_271_Closed_Image.style.display='inline'; Codehighlighter1_255_271_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_255_271_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_255_271_Closed_Text.style.display='none'; Codehighlighter1_255_271_Open_Image.style.display='inline'; Codehighlighter1_255_271_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_255_271_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_255_271_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span></div>
</div>
<p>3、最后的改进</p>
<p>使用OneWay固然可以解决上述的问题，但不够友好。因为对于广播消息的一方来说，象被蒙上了眼睛一样，对于客户端发生的事情懵然不知。这并不是一个好的idea。在Ingo Rammer的Advanced .NET Remoting一书中，Ingo Rammer先生提出了一个更好的办法，就是在发送信息一方时，检查了委托链。并在委托链的遍历中来捕获异常。当其中一个委托发生异常时，显示提示信息。然后继续遍历后面的委托，这样既保证了异常信息的提示，又保证了其他订阅者正常接收消息。因此，我对本例的远程对象进行了修改，注释掉[OneWay]，修改了BroadCastInfo()方法：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[OneWay]</span><span style="COLOR: #008000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;BroadCastingInfo(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;info)<br><img id=Codehighlighter1_57_586_Open_Image onclick="this.style.display='none'; Codehighlighter1_57_586_Open_Text.style.display='none'; Codehighlighter1_57_586_Closed_Image.style.display='inline'; Codehighlighter1_57_586_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_57_586_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_57_586_Closed_Text.style.display='none'; Codehighlighter1_57_586_Open_Image.style.display='inline'; Codehighlighter1_57_586_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_57_586_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_57_586_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(BroadCastEvent&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">)<br><img id=Codehighlighter1_93_513_Open_Image onclick="this.style.display='none'; Codehighlighter1_93_513_Open_Text.style.display='none'; Codehighlighter1_93_513_Closed_Image.style.display='inline'; Codehighlighter1_93_513_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_93_513_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_93_513_Closed_Text.style.display='none'; Codehighlighter1_93_513_Open_Image.style.display='inline'; Codehighlighter1_93_513_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_93_513_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_93_513_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BroadCastEventHandler&nbsp;tempEvent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;index&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">记录事件订阅者委托的索引，为方便标识，从1开始。</span><span style="COLOR: #008000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">foreach</span><span style="COLOR: #000000">&nbsp;(Delegate&nbsp;del&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;BroadCastEvent.GetInvocationList())<br><img id=Codehighlighter1_255_504_Open_Image onclick="this.style.display='none'; Codehighlighter1_255_504_Open_Text.style.display='none'; Codehighlighter1_255_504_Closed_Image.style.display='inline'; Codehighlighter1_255_504_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_255_504_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_255_504_Closed_Text.style.display='none'; Codehighlighter1_255_504_Open_Image.style.display='inline'; Codehighlighter1_255_504_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_255_504_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_255_504_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_271_347_Open_Image onclick="this.style.display='none'; Codehighlighter1_271_347_Open_Text.style.display='none'; Codehighlighter1_271_347_Closed_Image.style.display='inline'; Codehighlighter1_271_347_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_271_347_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_271_347_Closed_Text.style.display='none'; Codehighlighter1_271_347_Open_Image.style.display='inline'; Codehighlighter1_271_347_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_271_347_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_271_347_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempEvent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(BroadCastEventHandler)del;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempEvent(info);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_365_484_Open_Image onclick="this.style.display='none'; Codehighlighter1_365_484_Open_Text.style.display='none'; Codehighlighter1_365_484_Closed_Image.style.display='inline'; Codehighlighter1_365_484_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_365_484_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_365_484_Closed_Text.style.display='none'; Codehighlighter1_365_484_Open_Image.style.display='inline'; Codehighlighter1_365_484_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_365_484_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_365_484_Open_Text><span style="COLOR: #000000">{&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><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox.Show(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">事件订阅者</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;index.ToString()&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">发生错误,系统将取消事件订阅!</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BroadCastEvent&nbsp;</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">&nbsp;tempEvent;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;index</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_526_582_Open_Image onclick="this.style.display='none'; Codehighlighter1_526_582_Open_Text.style.display='none'; Codehighlighter1_526_582_Closed_Image.style.display='inline'; Codehighlighter1_526_582_Closed_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_526_582_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_526_582_Closed_Text.style.display='none'; Codehighlighter1_526_582_Open_Image.style.display='inline'; Codehighlighter1_526_582_Open_Text.style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_526_582_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_526_582_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox.Show(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">事件未被订阅或订阅发生错误!</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span></div>
</div>
<p>我们来试验一下。首先打开服务端，然后同时打开三个客户端。广播消息：</p>
<p><img height=375 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re12.gif" width=500 border=0></p>
<p>消息发送正常。</p>
<p>接着关闭其中一个客户端窗口，再广播消息（注意为模拟客户端异常情况，应在ClientForm_Closing方法中把第一步改进的取消订阅代码注释。否则不会发生异常。难道你真的愿意用断电来导致异常发生吗^_^），结果如图：</p>
<p><img height=375 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re13.gif" width=500 border=0></p>
<p>此时服务端报告了&#8220;事件订阅者1发生错误，系统将取消事件订阅&#8221;。注意此时另外两个客户端，还是和前面一样，只有两条广播信息。</p>
<p>当我们点击提示框的&#8220;确定&#8221;按钮后，广播仍然发送：</p>
<p><img height=375 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/re14.gif" width=500 border=0></p>
<p>通过这样的改进后，程序更加的完善，也更加的健壮和友好！</p>
<p><font color=#006400>附：<br>示例代码说明：<br>1、&nbsp;Remoting事件(客户端发传真)压缩包：为第一节内容；<br>2、&nbsp;Remoting事件(服务端广播)压缩包：为第二节、第三节内容，其中：<br>第二节代码包含于：<br>#region 客户端订阅服务端事件<br>#endregion<br>第三节代码包含于：<br>#region 客户端订阅客户端事件<br>#endregion<br>如果要实现第二节的程序，请注释掉第三节代码；反之亦然。示例程序默认为第二节程序。<br>3、&nbsp;运行示例程序时，请先运行服务端程序，然后运行客户端程序。否则会抛出&#8220;基础连接已关闭&#8221;的异常。<br>4、&nbsp;解决方案均放在Common（或ICommon）文件夹中。</font></p>
<p><font color=#a52a2a>5、改进后的代码放到Remoting事件(服务端广播改进)压缩包中，大家可以比较一下改进后的程序有何不同！</font></p>
<p>参考资料：<br>1、&nbsp;Ingo Rammer，《Advanced .NET Remoting》<br>2、&nbsp;吕震宇，《<a href="http://www.cnblogs.com/zhenyulu/articles/29357.html"><font color=#0066aa>利用Event松耦合远程对象与远程系统</font></a>》<br>3、&nbsp;大坏蛋，《<a href="http://www.cnblogs.com/dahuaidan410/articles/31656.html"><font color=#0066aa>.NET Remoting中的事件处理(.NET Framework 2.0)（一）</font></a>》</p>
<img src ="http://www.blogjava.net/superwei/aggbug/115911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-08 13:09 <a href="http://www.blogjava.net/superwei/articles/115911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Microsoft .Net Remoting系列专题之二：Marshal、Disconnect与生命周期以及跟踪服务（转载）</title><link>http://www.blogjava.net/superwei/articles/115901.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 08 May 2007 04:03:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/115901.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/115901.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/115901.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/115901.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/115901.html</trackback:ping><description><![CDATA[<p><font color=#a52a2a><strong><font color=#000000>Microsoft .Net Remoting系列专题之二</font></strong>&nbsp;</font></p>
<p><font color=#a52a2a><strong>一、远程对象的激活</strong></font></p>
<p>在Remoting中有三种激活方式，一般的实现是通过RemotingServices类的静态方法来完成。工作过程事实上是将该远程对象注册到通道中。由于Remoting没有提供与之对应的Unregister方法来注销远程对象，所以如果需要注册/注销指定对象，微软推荐使用Marshal（一般译为编组）和Disconnect配对使用。在《<a href="http://www.brucezhang.com/?p=70" rel=bookmark mce_href="http://www.brucezhang.com/?p=70"><font color=#0066aa>Net Remoting基础篇</font></a>》中我已经谈到：Marshal()方法是将MarshalByRefObject类对象转化为ObjRef类对象，这个对象是存储生成代理以与远程对象通讯所需的所有相关信息。这样就可以将该实例序列化以便在应用程序域之间以及通过网络进行传输，客户端就可以调用了。而Disconnect()方法则将具体的实例对象从通道中断开。</p>
<p>根据上述说明，Marshal()方法对远程对象以引用方式进行编组（Marshal-by-Reference，MBR），并将对象的代理信息放到通道中。客户端可以通过Activator.GetObject()来获取。如果用户要注销该对象，则通过调用Disconnect()方法。那么这种方式对于编组的远程对象是否存在生命周期的管理呢？这就是本文所要描述的问题。</p>
<p><font color=#a52a2a><strong>二、生命周期</strong></font></p>
<p>在CLR中，框架提供了GC（垃圾回收器）来管理内存中对象的生命周期。同样的，.Net Remoting使用了一种分布式垃圾回收，基于租用的形式来管理远程对象的生命周期。</p>
<p>早期的DCOM对于对象生命周期的管理是通过ping和引用计数来确定对象何时应当作为垃圾回收。然而ping引起的网络流量对分布式应用程序的性能是一种痛苦的负担，它大大地影响了分布式处理的整体性能。.Net Remoting在每个应用程序域中都引入一个租用管理器，为每个服务器端的SingleTon，或每个客户端激活的远程对象保存着对租用对象的引用。（说明：对于服务器端激活的SingleCall方式，由于它是无状态的，对于每个激活的远程对象，都由CLR的GC来自动回收，因此对于SingleCall模式激活的远程对象，不存在生命周期的管理。）</p>
<p><font color=#a52a2a>1、租用</font></p>
<p>租用是个封装了TimeSpan值的对象，用以管理远程对象的生存期。在.Net Remoting中提供了定义租用功能的ILease接口。当Remoting通过SingleTon模式或客户端激活模式来激活远程对象时，租用对象调用从System.MarshalByRefObject继承的InitializeLifetimeService方法，向对象请求租用。</p>
<p>ILease接口定义了有关生命周期的属性，均为TimeSpan值。如下：<br>InitialLeaseTime：初始化有效时间，默认值为300秒，如果为0，表示永不过期；<br>RenewOnCallTime：调用远程对象一个方法时的租用更新时间，默认值为120秒；<br>SponsorshipTimeout：超时值，通知Sponsor（发起人）租用过期后，Remoting会等待的时间，默认值为120秒；<br>CurrentLeaseTime：当前租用时间，首次获得租用时，为InitializeLeaseTime的值。</p>
<p>Remoting的远程对象因为继承了MarshalByRefObject，因此默认继承了InitializeLifetimeService方法，那么租用的相关属性为默认值。如果要改变这些设置，可以在远程对象中重写该方法。例如：<br>&nbsp;public override object InitializeLifetimeService()<br>&nbsp;{<br>&nbsp;&nbsp;ILease lease = (ILease)base.InitializeLifetimeService();<br>&nbsp;&nbsp;if (lease.CurrentState == LeaseState.Initial)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;lease.InitialLeaseTime = TimeSpan.FromMinutes(1);<br>&nbsp;&nbsp;&nbsp;lease.RenewOnCallTime = TimeSpan.FromSeconds(20);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return lease;&nbsp;&nbsp;<br>&nbsp;}</p>
<p>也可以忽略该方法，将对象的租用周期改变为无限：<br>&nbsp;public override object InitializeLifetimeService()<br>&nbsp;{<br>&nbsp;&nbsp;return null;<br>&nbsp;}</p>
<p><font color=#a52a2a>2、租用管理器</font></p>
<p>如果是前面所说的租用主要是应用在每个具体的远程对象上，那么租用管理器是服务器端专门用来管理远程对象生命周期的管理器，它维持着一个System.Hashtable成员，将租用映射为System.DateTime实例表示每个租用何时应过期。Remoting采用轮询的方式以一定的时间唤醒租用管理器，检查每个租用是否过期。默认为每10秒钟唤醒一次。轮询的间隔可以配置，如将轮询间隔设置为5分钟：LifetimeService.LeaseManagerPollTime = System.TimeSpan.FromMinutes(5);</p>
<p>我们还可以在租用管理器中设置远程对象租用的属性，如改变远程对象的初始有效时间为永久有效：<br>LifetimeServices.LeaseTime = TimeSpan.Zero;</p>
<p>我们也可以通过配置文件来设置生命周期，如：<br>&amp;lt;configuration&amp;gt;<br>&nbsp;&amp;lt;system.runtime.remoting&amp;gt;<br>&nbsp;&nbsp;&amp;lt;application name = "SimpleServer"&amp;gt;<br>&nbsp;&nbsp;&nbsp;&amp;lt;lifetime leaseTime = "0" sponsorshipTimeOut = "1M"&nbsp;renewOnCallTime = "1M" pollTime = "30S"/&amp;gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&amp;lt;/application&amp;gt;<br>&nbsp;&amp;lt;/system.runtime.remoting&amp;gt;<br>&amp;lt;/configuration&amp;gt;</p>
<p>注：配置文件中的pollTime即为上面所说的租用管理器的轮询间隔时间LeaseManagerPollTime。</p>
<p>租用管理器对于生命周期的设置是针对服务器上所有的远程对象。当我们通过配置文件或租用管理器设置租用的属性时，所有远程对象的生命周期都遵循该设置，除非我们对于指定的远程对象通过重写InitializeLifetimeService方法，改变了相关配置。也就是说，远程对象的租用配置优先级高于服务器端配置。</p>
<p><font color=#a52a2a>3、发起人（Sponsor）</font></p>
<p>发起人是针对客户端而言的。远程对象就是发起人要租用的对象，发起人可以与服务器端签订租约，约定租用时间。一旦到期后，发起人还可以续租，就像现实生活中租方的契约，房东、租房者之间的关系一样。</p>
<p>在.Net Framework中的System.Runtime.Remoting.Lifetime命名空间中定义了ClientSponsor类，该类继承了System.MarshalByRefObject，并实现了ISponsor接口。ClientSponsor类的属性和方法，可以参考MSDN。</p>
<p>客户端要使用发起人机制，必须创建ClientSponsor类的一个实例。然后调用相关方法如Register()或Renewal()方法来注册远程对象或延长生命周期。如：<br>RemotingObject obj = new RemotingObject();<br>ClientSponsor sponsor = new ClientSponsor();<br>sponsor.RenewalTime = TimeSpan.FromMinutes(2);<br>sponsor.Register(obj);</p>
<p>续租时间也可以在ClientSponsor的构造函数中直接设置，如：<br>ClientSponsor sponsor = new ClientSponsor(TimeSpan.FromMinutes(2));<br>sponsor.Register(obj);</p>
<p>我们也可以自己编写Sponsor来管理发起人机制，这个类必须继承ClientSponsor并实现ISponsor接口。</p>
<p><font color=#a52a2a><strong>三、跟踪服务</strong></font></p>
<p>如前所述，我们要判断通过Marshal编组远程对象是否存在生命周期的管理。在Remoting中，可以通过跟踪服务程序来监视MBR对象的编组进程。</p>
<p>我们可以创建一个简单的跟踪处理程序，该程序实现接口ITrackingHandler。接口ITrackingHandler定义了3个方法，MarshalObject、UnmarshalObject和DisconnectedObject。当远程对象被编组、解组和断开连接时，就会调用相应的方法。下面是该跟踪处理类的代码：public class MyTracking:ITrackingHandler<br>{<br>&nbsp;public MyTracking()<br>&nbsp;{<br>&nbsp;&nbsp;//<br>&nbsp;&nbsp;// TODO: 在此处添加构造函数逻辑<br>&nbsp;&nbsp;//<br>&nbsp;}</p>
<p>&nbsp;public void MarshaledObject(object obj,ObjRef or)<br>&nbsp;{<br>&nbsp;&nbsp;Console.WriteLine();<br>&nbsp;&nbsp;Console.WriteLine("对象" + obj.Tostring() + " is marshaled at " + DateTime.Now.ToShortTimeString());<br>&nbsp;}</p>
<p>&nbsp;public void UnmarshaledObject(object obj,ObjRef or)<br>&nbsp;{<br>&nbsp;&nbsp;Console.WriteLine();<br>&nbsp;&nbsp;Console.WriteLine("对象" + obj.Tostring() + " is unmarshaled at " + DateTime.Now.ToShortTimeString());<br>&nbsp;}</p>
<p>&nbsp;&nbsp;public void DisconnectedObject(object obj)<br>&nbsp;{<br>&nbsp;&nbsp;Console.WriteLine(obj.ToString() + " is disconnected at " + DateTime.Now.ToShortTimeString());<br>&nbsp;}<br>}</p>
<p>然后再服务器端创建该跟踪处理类的实例，并注册跟踪服务：<br>TrackingServices.RegisterTrackingHandler(new MyTracking());</p>
<p><font color=#a52a2a><strong>四、测试</strong></font></p>
<p><font color=#a52a2a>1、建立两个远程对象，并重写InitializeLifetimeService方法：</font></p>
<p>对象一：AppService1<br>初始生命周期：1分钟</p>
<p>&nbsp;public class AppService1:MarshalByRefObject<br>&nbsp;{<br>&nbsp;&nbsp;public void PrintString(string contents)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;Console.WriteLine(contents);&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;public override object InitializeLifetimeService()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;ILease lease = (ILease)base.InitializeLifetimeService();<br>&nbsp;&nbsp;&nbsp;if (lease.CurrentState == LeaseState.Initial)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;lease.InitialLeaseTime = TimeSpan.FromMinutes(1);<br>&nbsp;&nbsp;&nbsp;&nbsp;lease.RenewOnCallTime = TimeSpan.FromSeconds(20);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;return lease;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;}</p>
<p>对象二：AppService2<br>初始生命周期：3分钟</p>
<p>&nbsp;public class AppService2:MarshalByRefObject<br>&nbsp;{<br>&nbsp;&nbsp;public void PrintString(string contents)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;Console.WriteLine(contents);&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;public override object InitializeLifetimeService()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;ILease lease = (ILease)base.InitializeLifetimeService();<br>&nbsp;&nbsp;&nbsp;if (lease.CurrentState == LeaseState.Initial)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;lease.InitialLeaseTime = TimeSpan.FromMinutes(3);<br>&nbsp;&nbsp;&nbsp;&nbsp;lease.RenewOnCallTime = TimeSpan.FromSeconds(40);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;return lease;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;}</p>
<p>为简便起见，两个对象的方法都一样。</p>
<p><font color=#a52a2a>2、服务器端</font></p>
<p>(1) 首先建立如上的监控处理类；</p>
<p>(2) 注册通道：<br>TcpChannel channel = new TcpChannel(8080);<br>ChannelServices.RegisterChannel(channel);</p>
<p>(3) 设置租用管理器的初始租用时间为无限：<br>LifetimeServices.LeaseTime = TimeSpan.Zero;</p>
<p>(4) 创建该跟踪处理类的实例，并注册跟踪服务：<br>TrackingServices.RegisterTrackingHandler(new MyTracking());</p>
<p>(5) 编组两个远程对象：<br>ServerAS.AppService1 service1 = new ServerAS1.AppService1();<br>ObjRef objRef1 = RemotingServices.Marshal((MarshalByRefObject)service1,"AppService1");</p>
<p>ServerAS.AppService2 service2 = new ServerAS1.AppService2();<br>ObjRef objRef2 = RemotingServices.Marshal((MarshalByRefObject)service2,"AppService2");</p>
<p>(6) 使服务器端保持运行：<br>Console.WriteLine("Remoting服务启动，按退出...");&nbsp;<br>Console.ReadLine();</p>
<p><font color=#a52a2a>3、客户端</font></p>
<p>通过Activator.GetObject()获得两个远程对象，并调用其方法PrintString。代码略。</p>
<p><font color=#a52a2a>4、运行测试</font>：</p>
<p>运行服务器端和客户端，由于监控程序将监视远程对象的编组进程，因此在运行开始，就会显示远程对象已经被Marshal：</p>
<p align=center><img height=271 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/ar1.gif" width=668 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/ar1.gif"></p>
<p>然后再客户端调用这两个远程对象的PrintString方法，服务器端接受字符串：</p>
<p align=center><img height=303 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/ar2.gif" width=668 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/ar2.gif"></p>
<p>一分钟后，远程对象一自动被Disconnect：</p>
<p align=center><img height=303 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/ar3.gif" width=668 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/ar3.gif"></p>
<p>此时客户端如要调用远程对象一，会抛出RemotingException异常；</p>
<p>又一分钟后，远程对象二被Disconnect了：</p>
<p>align="center"&gt;<img height=303 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/ar4.gif" width=668 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/ar4.gif"></p>
<p>用户还可以根据这个代码测试RenewOnCallTime的时间是否正确。也即是说，在对象还未被Disconnect时，调用对象，则从调用对象的这一刻起，其生命周期不再是原来设定的初始有效时间值（InitialLeaseTime），而是租用更新时间值（RenewOnCallTime）。另外，如果这两个远程对象没有重写InitializeLifetimeService方法，则生命周期应为租用管理器所设定的值，为永久有效（设置为0）。那么这两个对象不会被自动Disconnect，除非我们显式指定关闭它的连接。当然，如果我们显式关闭连接，跟踪程序仍然会监视到它的变化，然后显示出来。<br></p>
<p><font color=#a52a2a><strong>五、结论</strong><br></font></p>
<p>通过我们的测试，其实结论已经很明显了。通过Marshal编组的对象要受到租用的生命周期所控制。注意对象被Disconnect，并不是指这个对象被GC回收，而是指这个对象保存在通道的相关代理信息被断开了，而对象本身仍然在服务器端存在。<br></p>
<p>所以我们通过Remoting提供服务，应根据实际情况指定远程对象的生命周期，如果不指定，则为Remoting默认的设定。要让所有的远程对象永久有效，可以通过配置文件或租用管理器将初始有效时间设为0。</p>
<img src ="http://www.blogjava.net/superwei/aggbug/115901.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-08 12:03 <a href="http://www.blogjava.net/superwei/articles/115901.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Microsoft .Net Remoting系列专题之一:.Net Remoting基础篇(转载)</title><link>http://www.blogjava.net/superwei/articles/115899.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Tue, 08 May 2007 04:01:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/115899.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/115899.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/115899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/115899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/115899.html</trackback:ping><description><![CDATA[<p><strong>Microsoft .Net Remoting系列专题之一</strong></p>
<p><strong>一、Remoting基础</strong></p>
<p>什么是Remoting，简而言之，我们可以将其看作是一种分布式处理方式。从微软的产品角度来看，可以说Remoting就是DCOM的一种升级，它改善了很多功能，并极好的融合到.Net平台下。Microsoft&#174; .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这也正是我们使用Remoting的原因。为什么呢？在Windows操作系统中，是将应用程序分离为单独的进程。这个进程形成了应用程序代码和数据周围的一道边界。如果不采用进程间通信（RPC）机制，则在一个进程中执行的代码就不能访问另一进程。这是一种操作系统对应用程序的保护机制。然而在某些情况下，我们需要跨过应用程序域，与另外的应用程序域进行通信，即穿越边界。</p>
<p>在Remoting中是通过通道（channel）来实现两个应用程序域之间对象的通信的。如图所示：</p>
<p align=center><img height=341 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/Remoting1.jpg" width=650 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/Remoting1.jpg"></p>
<p>首先，客户端通过Remoting，访问通道以获得服务端对象，再通过代理解析为客户端对象。这就提供一种可能性，即以服务的方式来发布服务器对象。远程对象代码可以运行在服务器上（如服务器激活的对象和客户端激活的对象），然后客户端再通过Remoting连接服务器，获得该服务对象并通过序列化在客户端运行。</p>
<p>在Remoting中，对于要传递的对象，设计者除了需要了解通道的类型和端口号之外，无需再了解数据包的格式。但必须注意的是，客户端在获取服务器端对象时，并不是获得实际的服务端对象，而是获得它的引用。这既保证了客户端和服务器端有关对象的松散耦合，同时也优化了通信的性能。</p>
<p><strong>1、Remoting的两种通道</strong></p>
<p>Remoting的通道主要有两种：Tcp和Http。在.Net中，System.Runtime.Remoting.Channel中定义了IChannel接口。IChannel接口包括了TcpChannel通道类型和Http通道类型。它们分别对应Remoting通道的这两种类型。</p>
<p>TcpChannel类型放在名字空间System.Runtime.Remoting.Channel.Tcp中。Tcp通道提供了基于Socket的传输工具，使用Tcp协议来跨越Remoting边界传输序列化的消息流。TcpChannel类型默认使用二进制格式序列化消息对象，因此它具有更高的传输性能。HttpChannel类型放在名字空间System.Runtime.Remoting.Channel.Http中。它提供了一种使用Http协议，使其能在Internet上穿越防火墙传输序列化消息流。默认情况下，HttpChannel类型使用Soap格式序列化消息对象，因此它具有更好的互操作性。通常在局域网内，我们更多地使用TcpChannel；如果要穿越防火墙，则使用HttpChannel。</p>
<p><strong>2、远程对象的激活方式</strong></p>
<p>在访问远程类型的一个对象实例之前，必须通过一个名为Activation的进程创建它并进行初始化。这种客户端通过通道来创建远程对象，称为对象的激活。在Remoting中，远程对象的激活分为两大类：服务器端激活和客户端激活。</p>
<p>(1) 服务器端激活，又叫做WellKnow方式，很多又翻译为知名对象。为什么称为知名对象激活模式呢？是因为服务器应用程序在激活对象实例之前会在一个众所周知的统一资源标识符(URI)上来发布这个类型。然后该服务器进程会为此类型配置一个WellKnown对象，并根据指定的端口或地址来发布对象。.Net Remoting把服务器端激活又分为SingleTon模式和SingleCall模式两种。</p>
<p>SingleTon模式：此为有状态模式。如果设置为SingleTon激活方式，则Remoting将为所有客户端建立同一个对象实例。当对象处于活动状态时，SingleTon实例会处理所有后来的客户端访问请求，而不管它们是同一个客户端，还是其他客户端。SingleTon实例将在方法调用中一直维持其状态。举例来说，如果一个远程对象有一个累加方法（i=0；++i），被多个客户端（例如两个）调用。如果设置为SingleTon方式，则第一个客户获得值为1，第二个客户获得值为2，因为他们获得的对象实例是相同的。如果熟悉Asp.Net的状态管理，我们可以认为它是一种Application状态。</p>
<p>SingleCall模式：SingleCall是一种无状态模式。一旦设置为SingleCall模式，则当客户端调用远程对象的方法时，Remoting会为每一个客户端建立一个远程对象实例，至于对象实例的销毁则是由GC自动管理的。同上一个例子而言，则访问远程对象的两个客户获得的都是1。我们仍然可以借鉴Asp.Net的状态管理，认为它是一种Session状态。</p>
<p>(2) 客户端激活。与WellKnown模式不同，Remoting在激活每个对象实例的时候，会给每个客户端激活的类型指派一个URI。客户端激活模式一旦获得客户端的请求，将为每一个客户端都建立一个实例引用。SingleCall模式和客户端激活模式是有区别的：首先，对象实例创建的时间不一样。客户端激活方式是客户一旦发出调用的请求，就实例化；而SingleCall则是要等到调用对象方法时再创建。其次，SingleCall模式激活的对象是无状态的，对象生命期的管理是由GC管理的，而客户端激活的对象则有状态，其生命周期可自定义。其三，两种激活模式在服务器端和客户端实现的方法不一样。尤其是在客户端，SingleCall模式是由GetObject()来激活，它调用对象默认的构造函数。而客户端激活模式，则通过CreateInstance()来激活，它可以传递参数，所以可以调用自定义的构造函数来创建实例。</p>
<p><strong>二、远程对象的定义</strong></p>
<p>前面讲到，客户端在获取服务器端对象时，并不是获得实际的服务端对象，而是获得它的引用。因此在Remoting中，对于远程对象有一些必须的定义规范要遵循。</p>
<p>由于Remoting传递的对象是以引用的方式，因此所传递的远程对象类必须继承MarshalByRefObject。MSDN对MarshalByRefObject的说明是：MarshalByRefObject 是那些通过使用代理交换消息来跨越应用程序域边界进行通信的对象的基类。不是从 MarshalByRefObject 继承的对象会以隐式方式按值封送。当远程应用程序引用一个按值封送的对象时，将跨越远程处理边界传递该对象的副本。因为您希望使用代理方法而不是副本方法进行通信，因此需要继承MarshallByRefObject。</p>
<p>以下是一个远程对象类的定义：<br>public class ServerObject:MarshalByRefObject<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Person GetPersonInfo(string name,string sex,int age)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person person = new Person();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Name = name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Sex = sex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Age = age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return person;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>这个类只实现了最简单的方法，就是设置一个人的基本信息，并返回一个Person类对象。注意这里返回的Person类。由于这里所传递的Person则是以传值的方式来完成的，而Remoting要求必须是引用的对象，所以必须将Person类序列化。</p>
<p>因此，在Remoting中的远程对象中，如果还要调用或传递某个对象，例如类，或者结构，则该类或结构则必须实现串行化Attribute[SerializableAttribute]：<br>[Serializable]<br>&nbsp;public class Person<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Person()<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; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string sex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int age;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get&nbsp;&nbsp;&nbsp; {return name;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set&nbsp;&nbsp;&nbsp; {name = value;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Sex<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get {return sex;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set {sex = value;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int Age<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get {return age;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set {age = value;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;}<br>将该远程对象以类库的方式编译成Dll。这个Dll将分别放在服务器端和客户端，以添加引用。</p>
<p>在Remoting中能够传递的远程对象可以是各种类型，包括复杂的DataSet对象，只要它能够被序列化。远程对象也可以包含事件，但服务器端对于事件的处理比较特殊，我将在本系列之三中介绍。</p>
<p><strong>三、服务器端</strong></p>
<p>根据第一部分所述，根据激活模式的不同，通道类型的不同服务器端的实现方式也有所不同。大体上说，服务器端应分为三步：</p>
<p><strong>1、注册通道</strong></p>
<p>要跨越应用程序域进行通信，必须实现通道。如前所述，Remoting提供了IChannel接口，分别包含TcpChannel和HttpChannel两种类型的通道。这两种类型除了性能和序列化数据的格式不同外，实现的方式完全一致，因此下面我们就以TcpChannel为例。</p>
<p>注册TcpChannel，首先要在项目中添加引用&#8220;System.Runtime.Remoting&#8221;，然后using名字空间：System.Runtime.Remoting.Channel.Tcp。代码如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TcpChannel channel = new TcpChannel(8080);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChannelServices.RegisterChannel(channel);</p>
<p>在实例化通道对象时，将端口号作为参数传递。然后再调用静态方法RegisterChannel()来注册该通道对象即可。</p>
<p><strong>2、注册远程对象</strong></p>
<p>注册了通道后，要能激活远程对象，必须在通道中注册该对象。根据激活模式的不同，注册对象的方法也不同。</p>
<p><strong>(1) SingleTon模式</strong></p>
<p>对于WellKnown对象，可以通过静态方法RemotingConfiguration.RegisterWellKnownServiceType()来实现：RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObject),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ServiceMessage",WellKnownObjectMode.SingleTon);</p>
<p><strong>(2)SingleCall模式</strong></p>
<p>注册对象的方法基本上和SingleTon模式相同，只需要将枚举参数WellKnownObjectMode改为SingleCall就可以了。RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObject),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ServiceMessage",WellKnownObjectMode.SingleCall);</p>
<p><strong>(3)客户端激活模式</strong></p>
<p>对于客户端激活模式，使用的方法又有不同，但区别不大，看了代码就一目了然。<br>RemotingConfiguration.ApplicationName = "ServiceMessage";<br>RemotingConfiguration.RegisterActivatedServiceType(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObject));</p>
<p>为什么要在注册对象方法前设置ApplicationName属性呢？其实这个属性就是该对象的URI。对于WellKnown模式，URI是放在RegisterWellKnownServiceType()方法的参数中，当然也可以拿出来专门对ApplicationName属性赋值。而RegisterActivatedServiceType()方法的重载中，没有ApplicationName的参数，所以必须分开。</p>
<p><strong>3、注销通道</strong></p>
<p>如果要关闭Remoting的服务，则需要注销通道，也可以关闭对通道的监听。在Remoting中当我们注册通道的时候，就自动开启了通道的监听。而如果关闭了对通道的监听，则该通道就无法接受客户端的请求，但通道仍然存在，如果你想再一次注册该通道，会抛出异常。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#008000>//获得当前已注册的通道；<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IChannel[] channels = ChannelServices.RegisteredChannels;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#008000>//关闭指定名为MyTcp的通道；</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (IChannel eachChannel in channels)<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; if (eachChannel.ChannelName == "MyTcp")<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;&nbsp;&nbsp;&nbsp;&nbsp; TcpChannel tcpChannel = (TcpChannel)eachChannel;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#008000> //关闭监听；</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tcpChannel.StopListening(null);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#008000> //注销通道；</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChannelServices.UnregisterChannel(tcpChannel);<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; }<br>代码中，RegisterdChannel属性获得的是当前已注册的通道。在Remoting中，是允许同时注册多个通道的，这一点会在后面说明。</p>
<p><strong>四、客户端</strong></p>
<p>客户端主要做两件事，一是注册通道。这一点从图一就可以看出，Remoting中服务器端和客户端都必须通过通道来传递消息，以获得远程对象。第二步则是获得该远程对象。</p>
<p><strong>1、注册通道：</strong><br>TcpChannel channel = new TcpChannel();<br>ChannelServices.RegisterChannel(channel);</p>
<p>注意在客户端实例化通道时，是调用的默认构造函数，即没有传递端口号。事实上，这个端口号是缺一不可的，只不过它的指定被放在后面作为了Uri的一部分。<br><strong><br>2、获得远程对象。</strong></p>
<p>与服务器端相同，不同的激活模式决定了客户端的实现方式也将不同。不过这个区别仅仅是WellKnown激活模式和客户端激活模式之间的区别，而对于SingleTon和SingleCall模式，客户端的实现完全相同。</p>
<p><strong>(1) WellKnown激活模式</strong></p>
<p>要获得服务器端的知名远程对象，可通过Activator进程的GetObject()方法来获得：<br>ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");</p>
<p>首先以WellKnown模式激活，客户端获得对象的方法是使用GetObject（）。其中参数第一个是远程对象的类型。第二个参数就是服务器端的uri。如果是http通道，自然是用<a href="http://localhost:8080/ServiceMessage" mce_href="http://localhost:8080/ServiceMessage"><font color=#0066aa>http://localhost:8080/ServiceMessage</font></a>了。因为我是用本地机，所以这里是localhost，你可以用具体的服务器IP地址来代替它。端口必须和服务器端的端口一致。后面则是服务器定义的远程对象服务名，即ApplicationName属性的内容。<br><strong><br>(2) 客户端激活模式</strong></p>
<p>如前所述，WellKnown模式在客户端创建对象时，只能调用默认的构造函数，上面的代码就说明了这一点，因为GetObject()方法不能传递构造函数的参数。而客户端激活模式则可以通过自定义的构造函数来创建远程对象。</p>
<p>客户端激活模式有两种方法：<br>1) 调用RemotingConfiguration的静态方法RegisterActivatedClientType()。这个方法返回值为Void，它只是将远程对象注册在客户端而已。具体的实例化还需要调用对象类的构造函数。<br>&nbsp;RemotingConfiguration.RegisterActivatedClientType(&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; typeof(ServerRemoteObject.ServerObject),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "tcp://localhost:8080/ServiceMessage");<br>&nbsp;ServerRemoteObject.ServerObject serverObj = new ServerRemoteObject.ServerObject();</p>
<p>2) 调用进程Activator的CreateInstance()方法。这个方法将创建方法参数指定类型的类对象。它与前面的GetObject()不同的是，它要在客户端调用构造函数，而GetObject()只是获得对象，而创建实例是在服务器端完成的。CreateInstance()方法有很多个重载，我着重说一下其中常用的两个。<br>a、&nbsp;public static object CreateInstance(Type type, object[] args, object[] activationAttributes);</p>
<p>参数说明：<br>type：要创建的对象的类型。<br>args ：与要调用构造函数的参数数量、顺序和类型匹配的参数数组。如果 args 为空数组或空引用（Visual Basic 中为 Nothing），则调用不带任何参数的构造函数（默认构造函数）。<br>activationAttributes ：包含一个或多个可以参与激活的属性的数组。</p>
<p>这里的参数args是一个object[]数组类型。它可以传递要创建对象的构造函数中的参数。从这里其实可以得到一个结论：WellKnown激活模式所传递的远程对象类，只能使用默认的构造函数；而Activated模式则可以用户自定义构造函数。activationAttributes参数在这个方法中通常用来传递服务器的url。<br>假设我们的远程对象类ServerObject有个构造函数：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerObject(string pName,string pSex,int pAge)<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; name = pName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sex = pSex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; age = pAge;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>那么实现的代码是：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessage")};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object[] objs = new object[3];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objs[0] = "wayfarer";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objs[1] = "male";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objs[2] = 28;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerRemoteObject.ServerObject = Activator.CreateInstance(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObject),objs,attrs);<br>可以看到，objs[]数组传递的就是构造函数的参数。</p>
<p>b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);</p>
<p>参数说明：<br>assemblyName ：将在其中查找名为 typeName 的类型的程序集的名称。如果 assemblyName 为空引用（Visual Basic 中为 Nothing），则搜索正在执行的程序集。<br>typeName：首选类型的名称。<br>activationAttributes ：包含一个或多个可以参与激活的属性的数组。</p>
<p>参数说明一目了然。注意这个方法返回值为ObjectHandle类型，因此代码与前不同：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject",<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; "ServerRemoteObject.ServerObject",attrs);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();</p>
<p>这个方法实际上是调用的默认构造函数。ObjectHandle.Unwrap()方法是返回被包装的对象。</p>
<p>说明：要使用UrlAttribute，还需要在命名空间中添加：using System.Runtime.Remoting.Activation;</p>
<p><strong>五、Remoting基础的补充</strong></p>
<p>通过上面的描述，基本上已经完成了一个最简单的Remoting程序。这是一个标准的创建Remoting程序的方法，但在实际开发过程中，我们遇到的情况也许千奇百怪，如果只掌握一种所谓的&#8220;标准&#8221;，就妄想可以&#8220;一招鲜、吃遍天&#8221;，是不可能的。</p>
<p><strong>1、注册多个通道</strong></p>
<p>在Remoting中，允许同时创建多个通道，即根据不同的端口创建不同的通道。但是，Remoting要求通道的名字必须不同，因为它要用来作为通道的唯一标识符。虽然IChannel有ChannelName属性，但这个属性是只读的。因此前面所述的创建通道的方法无法实现同时注册多个通道的要求。</p>
<p>这个时候，我们必须用到System.Collection中的IDictionary接口：</p>
<p>注册Tcp通道：<br>IDictionary tcpProp = new Hashtable();<br>tcpProp["name"] = "tcp9090";<br>tcpProp["port"] = 9090;<br>IChannel channel = new TcpChannel(tcpProp,<br>&nbsp;new BinaryClientFormatterSinkProvider(),<br>&nbsp;new BinaryServerFormatterSinkProvider());<br>ChannelServices.RegisterChannel(channel);</p>
<p>注册Http通道：<br>IDictionary httpProp = new Hashtable();<br>httpProp["name"] = "http8080";<br>httpProp["port"] = 8080;<br>IChannel channel = new HttpChannel(httpProp,<br>&nbsp;new SoapClientFormatterSinkProvider(),<br>&nbsp;new SoapServerFormatterSinkProvider());<br>ChannelServices.RegisterChannel(channel);</p>
<p>在name属性中，定义不同的通道名称就可以了。</p>
<p><strong>2、远程对象元数据相关性</strong></p>
<p>由于服务器端和客户端都要用到远程对象，通常的方式是生成两份完全相同的对象Dll，分别添加引用。不过为了代码的安全性，且降低客户端对远程对象元数据的相关性，我们有必要对这种方式进行改动。即在服务器端实现远程对象，而在客户端则删除这些实现的元数据。</p>
<p>由于激活模式的不同，在客户端创建对象的方法也不同，所以要分离元数据的相关性，也应分为两种情况。</p>
<p><strong>(1) WellKnown激活模式：</strong></p>
<p>通过接口来实现。在服务器端，提供接口和具体类的实现，而在客户端仅提供接口：<br>&nbsp;&nbsp;&nbsp; public interface IServerObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person GetPersonInfo(string name,string sex,int age);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>public class ServerObject:MarshalByRefObject,IServerObject<br>{ ......}<br>注意：两边生成该对象程序集的名字必须相同，严格地说，是命名空间的名字必须相同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>(2) 客户端激活模式：</strong></p>
<p>如前所述，对于客户端激活模式，不管是使用静态方法，还是使用CreateInstance()方法，都必须在客户端调用构造函数实例化对象。所以，在客户端我们提供的远程对象，就不能只提供接口，而没有类的实现。实际上，要做到与远程对象元数据的分离，可以由两种方法供选择：</p>
<p><strong>a、利用WellKnown激活模式模拟客户端激活模式：</strong></p>
<p>方法是利用设计模式中的&#8220;抽象工厂&#8221;，下面的类图表描述了总体解决方案：</p>
<p align=center><img height=278 src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/remoting2.gif" width=370 border=0 mce_src="http://www.blogjava.net/images/cnblogs_com/wayfarer/remoting2.gif"></p>
<p>我们在服务器端的远程对象中加上抽象工厂的接口和实现类：<br>&nbsp;&nbsp;&nbsp; public interface IServerObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person GetPersonInfo(string name,string sex,int age);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public interface IServerObjFactory<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IServerObject CreateInstance();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public class ServerObject:MarshalByRefObject,IServerObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Person GetPersonInfo(string name,string sex,int age)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person person = new Person();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Name = name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Sex = sex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Age = age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return person;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public class ServerObjFactory:MarshalByRefObject,IServerObjFactory<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IServerObject CreateInstance()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ServerObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>然后再客户端的远程对象中只提供工厂接口和原来的对象接口：<br>&nbsp;&nbsp;&nbsp; public interface IServerObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person GetPersonInfo(string name,string sex,int age);<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public interface IServerObjFactory<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IServerObject CreateInstance();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; }<br>我们用WellKnown激活模式注册远程对象，在服务器端：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //传递对象；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RemotingConfiguration.RegisterWellKnownServiceType(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.ServerObjFactory),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ServiceMessage",WellKnownObjectMode.SingleCall);</p>
<p>注意这里注册的不是ServerObject类对象，而是ServerObjFactory类对象。</p>
<p>客户端：<br>ServerRemoteObject.IServerObjFactory serverFactory =&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; (ServerRemoteObject.IServerObjFactory)&nbsp;Activator.GetObject(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeof(ServerRemoteObject.IServerObjFactory),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "tcp://localhost:8080/ServiceMessage");</p>
<p>ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();</p>
<p>为什么说这是一种客户端激活模式的模拟呢？从激活的方法来看，我们是使用了SingleCall模式来激活对象，但此时激活的并非我们要传递的远程对象，而是工厂对象。如果客户端要创建远程对象，还应该通过工厂对象的CreateInstance()方法来获得。而这个方法正是在客户端调用的。因此它的实现方式就等同于客户端激活模式。</p>
<p><strong>b、利用替代类来取代远程对象的元数据</strong></p>
<p>实际上，我们可以用一个trick，来欺骗Remoting。这里所说的替代类就是这个trick了。既然是提供服务，Remoting传递的远程对象其实现的细节当然是放在服务器端。而要在客户端放对象的副本，不过是因为客户端必须调用构造函数，而采取的无奈之举。既然具体的实现是在服务器端，又为了能在客户端实例化，那么在客户端就实现这些好了。至于实现的细节，就不用管了。</p>
<p>如果远程对象有方法，服务器端则提供方法实现，而客户端就提供这个方法就OK了，至于里面的实现，你可以是抛出一个异常，或者return 一个null值；如果方法返回void，那么里面可以是空。关键是这个客户端类对象要有这个方法。这个方法的实现，其实和方法的声明差不多，所以我说是一个trick。方法如是，构造函数也如此。</p>
<p>还是用代码来说明这种&#8220;阴谋&#8221;，更直观：</p>
<p>服务器端：<br>&nbsp;&nbsp;&nbsp; public class ServerObject:MarshalByRefObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ServerObject()<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; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Person GetPersonInfo(string name,string sex,int age)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Person person = new Person();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Name = name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Sex = sex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person.Age = age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return person;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>客户端：<br>&nbsp;&nbsp;&nbsp; public class ServerObject:MarshalByRefObject<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ServerObj()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new System.NotImplementedException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Person GetPersonInfo(string name,string sex,int age)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new System.NotImplementedException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>比较客户端和服务器端，客户端的方法GetPersonInfo()，没有具体的实现细节，只是抛出了一个异常。或者直接写上语句return null，照样OK。我们称客户端的这个类为远程对象的替代类。</p>
<p><strong>3、利用配置文件实现</strong></p>
<p>前面所述的方法，于服务器uri、端口、以及激活模式的设置是用代码来完成的。其实我们也可以用配置文件来设置。这样做有个好处，因为这个配置文件是Xml文档。如果需要改变端口或其他，我们就不需要修改程序，并重新编译，而是只需要改变这个配置文件即可。</p>
<p>(1) 服务器端的配置文件：<br>&amp;lt;configuration&amp;gt;<br>&nbsp; &amp;lt;system.runtime.remoting&amp;gt;<br>&nbsp;&nbsp;&nbsp; &amp;lt;application name="ServerRemoting"&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;service&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;wellknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;/service&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;channels&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;channel ref="tcp" port="8080"/&amp;gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;/channels&amp;gt;<br>&nbsp;&nbsp;&nbsp; &amp;lt;/application&amp;gt;<br>&nbsp; &amp;lt;/system.runtime.remoting&amp;gt;<br>&amp;lt;/configuration&amp;gt;</p>
<p>如果是客户端激活模式，则把wellknown改为activated，同时删除mode属性。</p>
<p>把该配置文件放到服务器程序的应用程序文件夹中，命名为ServerRemoting.config。那么前面的服务器端程序直接用这条语句即可：<br>RemotingConfiguration.Configure("ServerRemoting.config");</p>
<p>(2) 客户端配置文件</p>
<p>如果是客户端激活模式，修改和上面一样。调用也是使用RemotingConfiguration.Configure()方法来调用存储在客户端的配置文件。</p>
<p>配置文件还可以放在machine.config中。如果客户端程序是web应用程序，则可以放在web.config中。</p>
<p><strong>4、启动/关闭指定远程对象</strong></p>
<p>Remoting中没有提供类似UnregisterWellKnownServiceType()的方法，也即是说，一旦通过注册了远程对象，如果没有关闭通道的话，该对象就一直存在于通道中。只要客户端激活该对象，就会创建对象实例。如果Remoting传送的只有一个远程对象，这不存在问题，关闭通道就可以了。如果传送多个远程对象呢？要关闭指定的远程对象应该怎么做？关闭之后又需要启动又该如何？</p>
<p>我们注意到在Remoting中提供了Marshal()和Disconnect()方法，答案就在这里。Marshal()方法是将MarshalByRefObject类对象转化为ObjRef类对象，这个对象是存储生成代理以与远程对象通讯所需的所有相关信息。这样就可以将该实例序列化以便在应用程序域之间以及通过网络进行传输，客户端就可以调用了。而Disconnect()方法则将具体的实例对象从通道中断开。</p>
<p>方法如下：<br>首先注册通道：<br>TcpChannel channel = new TcpChannel(8080);<br>ChannelServices.RegisterChannel(channel);</p>
<p>接着启动服务：<br>先在服务器端实例化远程对象。<br>ServerObject obj = new ServerObject();</p>
<p>然后，注册该对象。注意这里不用RemotingConfiguration.RegisterWellKnownServiceType()，而是使用RemotingServices.Marshal()：</p>
<p>ObjRef objrefWellKnown = RemotingServices.Marshal(obj, "ServiceMessage");</p>
<p>如果要注销对象，则：<br>RemotingServices.Disconnect(obj);</p>
<p>要注意，这里Disconnect的类对象必须是前面实例化的对象。正因为此，我们可以根据需要创建指定的远程对象，而关闭时，则Disconnect之前实例化的对象。</p>
<p>至于客户端的调用，和前面WellKnown模式的方法相同，仍然是通过Activator.GetObject()来获得。但从实现代码来看，我们会注意到一个问题，由于服务器端是显式的实例化了远程对象，因此不管客户端有多少，是否相同，它们调用的都是同一个远程对象。因此我们将这个方法称为模拟的SingleTon模式。</p>
<p>客户端激活模式</p>
<p>我们也可以通过Marshal()和Disconnect()来模拟客户端激活模式。首先我们来回顾&#8220;远程对象元数据相关性&#8221;一节，在这一节中，我说到采用设计模式的&#8220;抽象工厂&#8221;来创建对象实例，以此用SingleCall模式来模拟客户端激活模式。在仔细想想前面的模拟的SingleTon模式。是不是答案就将呼之欲出呢？</p>
<p>在&#8220;模拟的SingleTon&#8221;模式中，我们是将具体的远程对象实例进行Marshal，以此让客户端获得该对象的引用信息。那么我们换一种思路，当我们用抽象工厂提供接口，工厂类实现创建远程对象的方法。然后我们在服务器端创建工厂类实例。再将这个工厂类实例进行Marshal。而客户端获取对象时，不是获取具体的远程对象，而是获取具体的工厂类对象。然后再调用CreateInstance()方法来创建具体的远程对象实例。此时，对于多个客户端而言，调用的是同一个工厂类对象；然而远程对象是在各个客户端自己创建的，因此对于远程对象而言，则是由客户端激活，创建的是不同对象了。</p>
<p>当我们要启动/关闭指定对象时，只需要用Disconnet()方法来注销工厂类对象就可以了。</p>
<p><strong>六、小结</strong></p>
<p>Microsoft.Net Remoting真可以说是博大精深。整个Remoting的内容不是我这一篇小文所能尽述的，更不是我这个Remoting的初学者所能掌握的。王国维在《人间词话》一书中写到：古今之成大事业大学问者，必经过三种境界。&#8220;昨夜西风凋碧树，独上高楼，望尽天涯路。&#8221;此第一境界也。&#8220;衣带渐宽终不悔，为伊消得人憔悴。&#8221;此第二境界也。&#8220;众里寻他千百度，蓦然回首，那人却在灯火阑珊处。&#8221;此第三境界也。如以此来形容我对Remoting的学习，还处于&#8220;独上高楼，望尽天涯路&#8221;的时候，真可以说还未曾登堂入室。</p>
<p>或许需得&#8220;衣带渐宽&#8221;，学得Remoting&#8220;终不悔&#8221;，方才可以&#8220;蓦然回首&#8221;吧。</p>
<img src ="http://www.blogjava.net/superwei/aggbug/115899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2007-05-08 12:01 <a href="http://www.blogjava.net/superwei/articles/115899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASCII（美国信息交换标准编码）表</title><link>http://www.blogjava.net/superwei/articles/67103.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Fri, 01 Sep 2006 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/67103.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/67103.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/67103.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/67103.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/67103.html</trackback:ping><description><![CDATA[
		<table bordercolor="#cccccc" cellspacing="1" cellpadding="0" width="730" align="center" bgcolor="#99ccff" border="0">
				<tbody>
						<tr bgcolor="#ffffff">
								<td width="100%" colspan="9">
										<p align="center">
												<strong>附录 </strong>
										</p>
								</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td width="100%" colspan="9">
										<p align="center">ASCII（美国信息交换标准编码）表 </p>
								</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td width="11%" rowspan="2">
										<p align="center">字符 </p>
								</td>
								<td width="22%" colspan="2">
										<p align="center">ASCII代码 </p>
								</td>
								<td width="11%" rowspan="2">
										<p align="center">字符 </p>
								</td>
								<td width="22%" colspan="2">
										<p align="center">ASCII代码 </p>
								</td>
								<td width="11%" rowspan="2">
										<p align="center">字符 </p>
								</td>
								<td width="23%" colspan="2">
										<p align="center">ASCII代码 </p>
								</td>
						</tr>
						<tr>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">二进制 </p>
								</td>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">十进制 </p>
								</td>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">二进制 </p>
								</td>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">十进制 </p>
								</td>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">二进制 </p>
								</td>
								<td width="11%" bgcolor="#ffffff">
										<p align="center">十进制 </p>
								</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td width="11%">
										<p align="center">回车 <br />ESC <br />空格 <br />！ <br />" <br /># <br />$ </p>
								</td>
								<td width="11%">
										<p align="center">0001101 <br />0011011 <br />0100000 <br />0100001 <br />0100010 <br />0100011 <br />0100100 </p>
								</td>
								<td width="11%">
										<p align="center">13 <br />27 <br />32 <br />33 <br />34 <br />35 <br />36 </p>
								</td>
								<td align="middle" width="11%">
										<p align="center">? <br />@ <br />A <br />B <br />C <br />D <br />E </p>
								</td>
								<td align="middle" width="11%">0111111 <br />1000000 <br />1000001 <br />1000010 <br />1000011 <br />1000100 <br />1000101 </td>
								<td align="middle" width="11%">
										<div align="center">63 <br />64 <br />65 <br />66 <br />67 <br />68 <br />69 </div>
								</td>
								<td align="middle" width="11%">a <br />b <br />c <br />d <br />e <br />f <br />g </td>
								<td align="middle" width="11%">
										<div align="center">1100001 <br />1100010 <br />1100011 <br />1100100 <br />1100101 <br />1100110 <br />1100111 </div>
								</td>
								<td align="middle" width="12%">97 <br />98 <br />99 <br />100 <br />101 <br />102 <br />103 </td>
						</tr>
						<tr bgcolor="#ffffff">
								<td width="11%">
										<p align="center">% <br />&amp; <br />, <br />( <br />) <br />* <br />+ </p>
								</td>
								<td width="11%">
										<p align="center">0100101 <br />0100110 <br />0100111 <br />0101000 <br />0101001 <br />0101010 <br />0101011 </p>
								</td>
								<td width="11%">
										<p align="center">37 <br />38 <br />39 <br />40 <br />41 <br />42 <br />43 </p>
								</td>
								<td align="middle" width="11%">
										<div align="center">F <br />G <br />H <br />I <br />J <br />K <br />L </div>
								</td>
								<td align="middle" width="11%">1000110 <br />1000111 <br />1001000 <br />1001001 <br />1001010 <br />1001011 <br />1001100 </td>
								<td align="middle" width="11%">
										<div align="center">70 <br />71 <br />72 <br />73 <br />74 <br />75 <br />76 </div>
								</td>
								<td align="middle" width="11%">h <br />i <br />j <br />k <br />l <br />m <br />n </td>
								<td align="middle" width="11%">
										<div align="center">1101000 <br />1101001 <br />1101010 <br />1101011 <br />1101100 <br />1101101 <br />1101110 </div>
								</td>
								<td align="middle" width="12%">104 <br />105 <br />106 <br />107 <br />108 <br />109 <br />110 </td>
						</tr>
						<tr bgcolor="#ffffff">
								<td width="11%">
										<p align="center">, <br />- <br />. <br />/ <br />0 <br />1 <br />2 </p>
								</td>
								<td width="11%">
										<p align="center">0101100 <br />0101101 <br />0101110 <br />0101111 <br />0110000 <br />0110001 <br />0110010 </p>
								</td>
								<td width="11%">
										<p align="center">44 <br />45 <br />46 <br />47 <br />48 <br />49 <br />50 </p>
								</td>
								<td align="middle" width="11%">
										<div align="center">M <br />N <br />O <br />P <br />Q <br />R <br />S </div>
								</td>
								<td align="middle" width="11%">1001101 <br />1001110 <br />1001111 <br />1010000 <br />1010001 <br />1010010 <br />1010011 </td>
								<td align="middle" width="11%">
										<div align="center">77 <br />78 <br />79 <br />80 <br />81 <br />82 <br />83 </div>
								</td>
								<td align="middle" width="11%">o <br />p <br />q <br />r <br />s <br />t <br />u </td>
								<td align="middle" width="11%">
										<div align="center">1101111 <br />1110000 <br />1110001 <br />1110010 <br />1110011 <br />1110100 <br />1110101 </div>
								</td>
								<td align="middle" width="12%">111 <br />112 <br />113 <br />114 <br />115 <br />116 <br />117 </td>
						</tr>
						<tr bgcolor="#ffffff">
								<td align="middle" width="11%">
										<div align="center">3 <br />4 <br />5 <br />6 <br />7 <br />8 </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">0110011 <br />0110100 <br />0110101 <br />0110110 <br />0110111 <br />0111000 </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">51 <br />52 <br />53 <br />54 <br />55 <br />56 </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">T <br />U <br />V <br />W <br />X <br />Y </div>
								</td>
								<td align="middle" width="11%">1010100 <br />1010101 <br />1010110 <br />1010111 <br />1011000 <br />1011001 </td>
								<td align="middle" width="11%">
										<div align="center">84 <br />85 <br />86 <br />87 <br />88 <br />89 </div>
								</td>
								<td valign="top" align="middle" width="11%">
										<div align="center">v <br />w <br />x <br />y <br />z </div>
								</td>
								<td valign="top" align="middle" width="11%">
										<div align="center">1110110 <br />1110111 <br />1111000 <br />1111001 <br />1111010 </div>
								</td>
								<td valign="top" align="middle" width="12%">118 <br />119 <br />120 <br />121 <br />122 </td>
						</tr>
						<tr bgcolor="#ffffff">
								<td align="middle" width="11%">
										<div align="center">9 <br />: <br />; <br />&lt; <br />= <br />&gt; </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">0111001 <br />0111010 <br />0111011 <br />0111100 <br />0111101 <br />0111110 </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">57 <br />58 <br />59 <br />60 <br />61 <br />62 </div>
								</td>
								<td align="middle" width="11%">
										<div align="center">Z <br />[ <br />\ <br />] <br />^ <br />- </div>
								</td>
								<td align="middle" width="11%">1011010 <br />1011011 <br />1011100 <br />1011101 <br />1011110 <br />1011111 </td>
								<td align="middle" width="11%">
										<div align="center">90 <br />91 <br />92 <br />93 <br />94 <br />95 </div>
								</td>
								<td valign="top" align="middle" width="11%">
										<div align="center">{ <br />| <br />} </div>
								</td>
								<td valign="top" align="middle" width="11%">
										<div align="center">1111011 <br />1111100 <br />1111101 </div>
								</td>
								<td valign="top" align="middle" width="12%">123 <br />124 <br />125 </td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/superwei/aggbug/67103.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2006-09-01 14:41 <a href="http://www.blogjava.net/superwei/articles/67103.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>分布式应用程序概述 </title><link>http://www.blogjava.net/superwei/articles/34447.html</link><dc:creator>小辞猬</dc:creator><author>小辞猬</author><pubDate>Thu, 09 Mar 2006 03:57:00 GMT</pubDate><guid>http://www.blogjava.net/superwei/articles/34447.html</guid><wfw:comment>http://www.blogjava.net/superwei/comments/34447.html</wfw:comment><comments>http://www.blogjava.net/superwei/articles/34447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/superwei/comments/commentRss/34447.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/superwei/services/trackbacks/34447.html</trackback:ping><description><![CDATA[<TABLE border=0 cellPadding=0 cellSpacing=0 class=zhtable width="100%">
<TBODY>
<TR>
<TD class=f14>分布式应用程序概述 <BR><BR>应用程序设计师可以使用 .NET 平台开发、部署和支持分布式应用程序。既高度集成又灵活，此平台使开发人员能够生成端对端的业务解决方案，这些解决方案可以综合利用现有的体系结构和应用程序。 <BR><BR>Windows DNA 是一种用于生成紧耦合分布式 Web 应用程序的体系结构。因为分布式应用程序开始要求更多的松耦合原则，Microsoft 体系结构移动到 .NET 平台上。 <BR><BR>基本原理和好处 <BR>分布式应用程序的主要原则是将应用程序逻辑地分为 3 个基本层： <BR><BR>表示 <BR>业务逻辑 <BR>数据访问和存储 <BR>通过按照这些原则对应用程序进行分层，使用基于组件的编程技术，并充分利用 .NET 平台与 Microsoft Windows 操作系统的功能，开发人员可以生成具有高度可伸缩性和灵活性的应用程序。 <BR><BR>简单地分布式应用程序模型包含与中间层进行通信的客户端，中间层本身由应用程序服务器和包含业务逻辑的应用程序组成。应用程序反过来又与提供和存储数据的数据库进行通信。 <BR><BR>表示服务 <BR>表示层包括到应用程序的胖客户端接口或者瘦客户端接口。胖客户端通过直接使用 Microsoft Win32 API 或间接通过 Windows 窗体，为操作系统的功能提供完全的编程接口，并广泛地使用组件。瘦客户端（Web 浏览器）正迅速成为许多开发人员优先选择的接口。开发人员能够生成可在三个应用程序层的任何一个上执行的业务逻辑。利用 ASP.NET Web 应用程序和 XML Web services，瘦客户端能够以可视形式为应用程序提供丰富、灵活和交互的用户界面。瘦客户端还具有在平台之间提供更大程度的可移植性的优点。 <BR><BR>业务逻辑/应用程序服务 <BR>该层被分为应用程序服务器与服务，它们可用于支持客户端。可以使用 .NET Framework 编写 Web 应用程序以利用 COM+ 服务、消息队列 (MSMQ)、目录服务和安全性服务。应用程序服务反过来可以与数据访问层上的若干个数据服务进行交互。 <BR><BR>数据访问和存储服务 <BR>支持数据访问和存储的数据服务包括下列各项： <BR><BR>ADO.NET，通过使用脚本语言或编程语言提供对数据的简化编程访问。 <BR>OLE DB，由 Microsoft 开发的公认的通用数据提供程序。 <BR>XML，用于指定数据结构的标记标准。 <BR>XML 是 Internet 世界提出的标准。HTML 集中于信息如何由浏览器呈现和如何显示在屏幕上，而 XML 的目标是处理数据结构及其表示形式。 <BR><BR>系统服务 <BR>该模型的每个部分中的元素都充分受到 .NET Framework 和 Windows 操作系统的支持。它所具有的许多服务中的一些是：目录、安全、管理和跨越 3 个层进行的通信服务。组成 Visual Studio .NET 开发系统的编程工具使开发人员能够生成跨越多层的应用程序组件。</TD><TD>jflakjds</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/superwei/aggbug/34447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/superwei/" target="_blank">小辞猬</a> 2006-03-09 11:57 <a href="http://www.blogjava.net/superwei/articles/34447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>