<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_为祖国健康工作50年</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/61831/rss</id><updated>2011-09-30T14:47:36Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/61831/rss"/><entry><id>http://www.cnblogs.com/lerit/archive/2011/09/30/2196877.html</id><title type="text">让TortoiseHg自动保存同步密码的几种方法</title><summary type="text">TortoiseHg是一个跨平台的Mercurial分布式版本控制系统的可视化客户端工具，如果使用https协议与服务器进行代码同步时是要求输入密码的，这比较烦人。这里根据用户对密码保存安全性的不同要求，总结了几种方法，以便在同步时不需要输入密码信息。以下均以TortoiseHg 2.0中文版为例。 1.非加密方式 非加密方式就是将同步密码直接置于配置文件中，这种方式设置快速，但是不利于密码的安...</summary><published>2011-09-30T14:46:00Z</published><updated>2011-09-30T14:46:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/09/30/2196877.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/09/30/2196877.html"/><content type="html">&lt;p&gt;TortoiseHg是一个跨平台的&lt;a href="http://www.oschina.net/p/mercurial"&gt;Mercurial&lt;/a&gt;分布式版本控制系统的可视化客户端工具，如果使用https协议与服务器进行代码同步时是要求输入密码的，这比较烦人。这里根据用户对密码保存安全性的不同要求，总结了几种方法，以便在同步时不需要输入密码信息。以下均以TortoiseHg 2.0中文版为例。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1.非加密方式&lt;/strong&gt; &lt;/p&gt; &lt;p&gt;非加密方式就是将同步密码直接置于配置文件中，这种方式设置快速，但是不利于密码的安全，有如下几种设置方式：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（1）第一次克隆时设置&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当需要在一个文件夹中第一次克隆服务器的代码时，在如下界面中的“源”地址中，包含用户名和密码即可：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/20110930224537573.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图01" border="0" alt="截图01" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245408390.jpg" width="612" height="179"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;其中username和password分别替换成实际的用户名和密码即可。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（2）在配置文件中设置&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;实际上上面的方法设置后，这些字符串保存在了配置文件中，如果第一次没有如上的输入密码信息，也可以通过之后修改配置文件来达到目的。这个配置文件在目标文件夹中.hg文件夹中叫做hgrc的文件（类似于asp.net中web.config，每个克隆的代码库，都有一个hgrc文件，它的设置只对于当前代码库有效），用文本打开即可显示配置信息（可以通过在文件夹中右键-TortoiseHg-仓库设置，然后选择右上角的“编辑文件”按钮打开）：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245421539.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图01" border="0" alt="截图01" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245448833.jpg" width="611" height="133"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;上面paths分支下的default就是我们上面（1）中设置的“源”，如果之前没有设置，随时都可以在这里加入用户名和密码信息。&lt;/p&gt; &lt;p&gt;另外，如果TortoiseHg基于的Mercurial版本是1.3及其以上（TortoiseHg 2.0基于Mercurial 1.8，因此可以），上面的配置文件也可以如下方式配置：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245462746.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图02" border="0" alt="截图02" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245484434.jpg" width="608" height="178"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;也就是将同步的用户名和密码信息放在auth节中。要注意，auth中三行一个也不能少，第一行是同步的url前缀信息，第二行用户名，第三行密码，其中的xxx，可是是任意合法的字符串，只要保证三行的xxx一样即可。虽然auth和paths中的prefix和default看似有些重复，但是一个也不能少。&lt;/p&gt; &lt;p&gt;可以这样翻译auth段：在当前代码库中，所有以http://bitbucket.org开头的url（记为xxx）同步时，都是用username作为用户名，password作为密码。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;虽然是明文在配置文件中保存了密码，但是在workbench中这些密码将会是星号方式显示，但有一个例外，也是一个bug，即如果密码中包含@字符，那么密码中从这个字符@开始一直到结束，都将不幸的显示在workbench中，不仅暴露部分密码，而且会导致同步的url被解析错误而无法同步。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/20110930224551200.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图00" border="0" alt="截图00" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302245555659.jpg" width="610" height="191"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;如图可以看出，如果密码是&lt;em&gt;&lt;a href="mailto:pass@word"&gt;pass@word&lt;/a&gt;&lt;/em&gt;，那么&lt;a href="mailto:'@word&amp;rsquo;"&gt;'@word’&lt;/a&gt;将被会明文显示出来，而@前面的pass还是正常的星号显示，这主要是因为密码中的@被解析为'&lt;strong&gt;用户名：密码@url’&lt;/strong&gt;中的那个@了，也就是对整个url进行了不正确的分割。因此如果密码是这种情况，这样设置密码的方式就无效了。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（3）全局设置&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;上面已经看到，对每一个代码库，如果都将用户和密码信息放在hgrc中，那么密码将分布在很多地方，安全性是最低的。还好，TortoiseHg提供了一个全局设置，类似于asp.net的machine.config文件，对于所有代码库，都生效，这个文件在windows系统中，一般是mercurial.ini文件（可以通过在文件夹中右键-TortoiseHg-全局设置，然后选择右上角的“编辑文件”按钮打开）&lt;/p&gt; &lt;p&gt;Hg全局配置文件位置  &lt;ul&gt; &lt;li&gt; &lt;p&gt;在XP 中一般是:&lt;tt&gt;C:\Documents and Settings\Administrator\mercurial.ini&lt;/tt&gt;&lt;/p&gt; &lt;li&gt; &lt;p&gt;在Windows 其它版本中,一般自动生成在你的用户文档目录中:&lt;tt&gt;C:\Documents and Settings\{用户名}\mercurial.ini&lt;/tt&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt; 可以用如下类似方式配置：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302246004292.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图01" border="0" alt="截图01" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109302246044353.jpg" width="510" height="227"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;翻译过来的意思就是说：以后凡是本机中代码库中url以https://bitbucket.org/foo/开头的，都用foo和foo_passwd分别作为用户名和密码，凡是以&lt;code&gt;https://bitbucket.org/bar/开头的，都用bar和bar_passwd作为用户名和密码。&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;通过这种方式，将所有密码信息都保存在一个文件中了，减少了密码配置的工作量。配置的详细信息还可以参考《&lt;/code&gt;&lt;a href="http://hgtip.com/tips/advanced/2009-10-01-configuring-user-auth-https/"&gt;Configuring User Auth for HTTPS&lt;/a&gt;》。&lt;/p&gt; &lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;strong&gt; 2.加密方式&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;以上方法提供的方式都是将密码以非加密方式保存，安全性比较低，还好，自从TortoiseHg 0.10，就提供了一个&lt;strong&gt;mercurial-keyring&lt;/strong&gt;扩展，可以将密码以加密方式保存在一个密码数据库中。&lt;/p&gt; &lt;p&gt;而我们要做的唯一一步操作，就是启用这个扩展。方法如下，只要在配置文件中（如果想让这个扩展只对某一个代码库生效，就修改相应目录下.hg的hgrc文件，若要使扩展对所有代码库都生效就找到mercurial.ini文件）加入以下字符串：&lt;/p&gt;[extensions]&lt;br/&gt;mercurial_keyring=当然也可以在TortoiseHg &amp;gt;仓库设置/全局设置&amp;gt; 扩展中，选择mercurial_keyring来实现。&lt;p&gt;因为是启用扩展，因此TortoiseHg软件需要重启，还好这一切都是他自己自动完成的。&lt;/p&gt;&lt;p&gt;通过以上操作，然后按照第一种方法中介绍的去配置hgrc文件，记住，不需要再在文件中指定密码，但是必须有用户名信息（在auth或者path中设置用户名均可）。这样，&lt;strong&gt;在第一次同步时，要求输入密码，以后就不再需要输入了&lt;/strong&gt;，这个密码会将以加密方式进行保存，这大大提高密码安全性。另外，如果密码更改了，这个扩展还会弹出输入密码的框从而更新密码信息，而上面的配置修改方式，如果设置的密码错误，则只会报错不会要求更新。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;备注1：&lt;/strong&gt;以上所涉及的配置文件，都不会随着同步操作被传送出去，因此里面的配置信息(尤其是密码)相对外界是是安全的，上述所指的不安全，是基于本地计算机而言。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;备注2：&lt;/strong&gt;TortoiseHg是Mercurial的一个优秀的gui客户端，以上介绍均基于TortoiseHg的，有些可以手动修改配置文件，而有些直接就可以在gui界面中实现。如果直接使用Mercurial，可能配置过程不同，会在命令模式下，并且一些扩展是需要手动安装然后启用的，可以查询&lt;a href="http://mercurial.selenic.com/wiki/KeyringExtension"&gt;相关资料&lt;/a&gt;进行配置。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;备注&lt;/strong&gt;3：&lt;/strong&gt;实际上，Mercurial 完全支持SSH 来进行同步，而不需要提供密码，这大大提高了安全性，不过还是需要一些其他配置，不在本文介绍范围，可以参考《&lt;a href="http://confluence.atlassian.com/display/BITBUCKET/Using+SSH+to+Access+your+Bitbucket+Repository"&gt;Using SSH to Access your Bitbucket Repository&lt;/a&gt;》&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2196877.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/09/30/2196877.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/09/26/2191984.html</id><title type="text">解决sqlserver安装时1608或1605错误提示</title><summary type="text">在一次卸载sqlserver后，发现无法重装了，提示信息：TITLE: SQL Server Setup failure. ------------------------------SQL Server Setup has encountered the following error:MsiGetProductInfo failed to retrieve ProductVersion for package with Product Code = '{2243F21A-E132-44F7-BA13-024D0845C815}'. Error code: 1605.goog</summary><published>2011-09-26T09:06:00Z</published><updated>2011-09-26T09:06:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191984.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191984.html"/><content type="html">&lt;p&gt;&lt;span style="font-size: 14px;"&gt;在一次卸载sqlserver后，发现无法重装了，提示信息：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;TITLE: SQL Server Setup failure. &lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 14px;"&gt;------------------------------&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;SQL Server Setup has encountered the following error:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;MsiGetProductInfo failed to retrieve ProductVersion for package with Product Code = '{2243F21A-E132-44F7-BA13-024D0845C815}'. Error code: 1605.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;google了半天，有些说可能卸载不完整，于是下来sqlserver清除注册表的工具，依然不行。后来看一些文章最后结论都是只能重装系统，没有别的方法。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;还好在一些英文论坛中，终于发现如下方法可以解决：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;1.根据提示信息中的Product Code，将第一段中字符串倒置，比如上面的得到的就是A12F3422；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;2.在注册表中HKCR\Installer\UpgradeCodes搜索上面字符串，找到后删除它的父项；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;3.重装sqlserver。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;如果操作完毕重装依然出现错误提示，有可能是Product Code已经变了（也就是说不止一个问题），需要重复以上步骤，直到成功安装为止。（这也是为什么经常有人试过这个方法后还是提示错误信息的原因，因为残留的值不止一个）。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;&lt;strong&gt;注册表修改有一定危险性，应该做好备份，删除前最好将相关项导出以备恢复之用。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;另外这个给出另外两篇文章，如果以上解决方案依然无法解决，或对于更复杂的一些问题，比如注册表权限导致的，以及Installation&amp;nbsp; Codes 和Product Codes的关系进行了介绍，有助于解决更为复杂的问题，希望有所帮助。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;&lt;span size="2"&gt;&lt;a href="http://social.msdn.microsoft.com/forums/en-US/sqlsetupandupgrade/thread/9a750065-4a6f-4bc9-8680-1c98d2b32fec/"&gt;《Recover manually from SQL Server 2008 installation failure》&lt;/a&gt;&lt;/span&gt;&lt;span size="2"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;&lt;a href="http://blogs.msdn.com/b/amruthavarshinij/archive/2010/11/03/unable-to-install-sql-2008-due-to-the-remnants-of-a-previous-installation.aspx"&gt;&lt;span size="2"&gt;《Unable to install SQL 2008 due to the remnants of a previous installation》&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="position: absolute; z-index: 99999999; display: none; top: 0pt; right: 0pt; bottom: auto; left: auto; height: auto; width: auto; padding: 5px; background-color: #000000; font-size: 13px; line-height: 1.2; color: #ffffff; opacity: 0.9;"&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div style="position: absolute; z-index: 99999999; display: none; top: 0pt; right: 0pt; bottom: auto; left: auto; height: auto; width: auto; padding: 5px; background-color: #000000; font-size: 13px; line-height: 1.2; color: #ffffff; opacity: 0.9;"&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2191984.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191984.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/09/26/2191767.html</id><title type="text">ghost XP SP3无法安装IIS的解决方法</title><summary type="text">关于 ghost xp sp3，网上有许多人问关于无法安装IIS的问题，有的说是因为ghost停止了一些服务造成的，有些说是因为sp3的一个补丁包造成的，这次遇到了同样的问题，在安装iis过程中总是提示找不到文件，试过了上面介绍的一些方法，却依然无法安装成功，最后在一些文章中说是windows游戏造成的，果然有效，方法如下： 添加删除程序-Windows组件向导，找到附件和工具，在其详细信息中去掉...</summary><published>2011-09-26T07:39:00Z</published><updated>2011-09-26T07:39:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191767.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191767.html"/><content type="html">&lt;p&gt;关于 ghost xp sp3，网上有许多人问关于无法安装IIS的问题，有的说是因为ghost停止了一些服务造成的，有些说是因为sp3的一个补丁包造成的，这次遇到了同样的问题，在安装iis过程中总是提示找不到文件，试过了上面介绍的一些方法，却依然无法安装成功，最后在一些文章中说是windows游戏造成的，果然有效，方法如下：&lt;/p&gt; &lt;p&gt;添加删除程序-Windows组件向导，找到附件和工具，在其详细信息中去掉“游戏”的对勾。然后重新安装iis即可。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2191767.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/09/26/2191767.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/09/16/2178431.html</id><title type="text">解决firefox的xmarks无法同步问题</title><summary type="text">家里和单位各一台电脑，使用firefox的xmarks作为书签同步工具。单位的系统恢复了一下到6月份了，书签是那个时候的了，想同步到最新，但是总无法成功。前一阵子是因为xmarks的服务器ip疑似被墙而无法更新，这个在hosts文件里做一些设置即可，这次不同，在firefox的工具-xmarks-设置，选择“马上同步”进行手动同步，提示如下错误： failed to converge.xmarks...</summary><published>2011-09-16T02:57:00Z</published><updated>2011-09-16T02:57:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/09/16/2178431.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/09/16/2178431.html"/><content type="html">&lt;p&gt;家里和单位各一台电脑，使用firefox的xmarks作为书签同步工具。单位的系统恢复了一下到6月份了，书签是那个时候的了，想同步到最新，但是总无法成功。前一阵子是因为xmarks的服务器ip疑似被墙而无法更新，这个在hosts文件里做一些设置即可，这次不同，在firefox的工具-xmarks-设置，选择“马上同步”进行手动同步，提示如下错误：&lt;/p&gt; &lt;p&gt;failed to converge.xmarks can’t properly merge changes…….&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109161057001331.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图00" border="0" alt="截图00" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109161057012793.jpg" width="373" height="125"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;可以看出，应该是由于在多台电脑使用的xmarks，而这些版本又不一致，其中存在一些无法合并的信息而导致的。这和代码版本控制软件类似，有些合并需要手动处理。&lt;/p&gt; &lt;p&gt;这里就不合并了，因为家里是最新的，最简单解决方法就是放弃这一台的版本，强制下载最新的版本进行覆盖：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;方法1：&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;工具-xmarks-设置-运行安装向导，登录账号后，一直下一步，在下图中选中：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109161057018366.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图01" border="0" alt="截图01" src="http://images.cnblogs.com/cnblogs_com/lerit/201109/201109161057019478.jpg" width="420" height="127"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;这样就覆盖本地的书签了。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;方法2：&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;也可以按照提示信息，工具-xmarks-设置-高级中，选择“强制覆盖本地书签”的下载按钮，按照提示覆盖即可。&lt;/p&gt; &lt;p&gt;如果需要合并的话，其实选择上面图中第一样即可。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2178431.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/09/16/2178431.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/08/22/2148858.html</id><title type="text">给没有源代码的.NET程序&amp;ldquo;打补丁&amp;rdquo;</title><summary type="text">公司为一个web应用程序写了一个注册机，基本原理是用户运行这个软件后，得到一个申请码，然后公司根据这个申请码给出相应注册码，匹配后方可正常使用web软件。在别人机子上没有问题，但是我机子上运行软件后死活就是没有申请码产生，也没报错。开发此程序的人员早不知道是谁了，也没有源码，只好自己分析是什么问题导致的，如果是程序的问题，希望能给程序打个“补丁”，准确的说是采用比较初级的.NET逆向工程来注入需要...</summary><published>2011-08-22T01:11:00Z</published><updated>2011-08-22T01:11:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/08/22/2148858.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/08/22/2148858.html"/><content type="html">&lt;p&gt;公司为一个web应用程序写了一个注册机，基本原理是用户运行这个软件后，得到一个申请码，然后公司根据这个申请码给出相应注册码，匹配后方可正常使用web软件。在别人机子上没有问题，但是我机子上运行软件后死活就是没有申请码产生，也没报错。开发此程序的人员早不知道是谁了，也没有源码，只好自己分析是什么问题导致的，如果是程序的问题，希望能给程序打个“补丁”，准确的说是采用比较初级的.NET&lt;strong&gt;逆向工程&lt;/strong&gt;来注入需要的补丁代码。以下是思路和主要操作（代码中略去了不需要的代码部分）。  &lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt;  &lt;p&gt;用reflector打开后，发现是.NET程序，且没混淆，这就好办了。因为程序的代码比较少，在reflector中看就那么几个按钮的事件函数，读一下就差不多了。&lt;strong&gt;基本思路&lt;/strong&gt;是，既然申请码那个文本框没有产生申请码，那么就要找到为那个申请框赋值的语句，看看那个语句有没有问题，那么要想找到这个语句，就要先找到这个申请框在程序中的ID号。  &lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt;  &lt;p&gt;从程序中填写完注册码后点击的那个注册按钮入手，那个按钮点击后提示了“注册码错误”，根据这几个字，找到了：  &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_99342" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_99342').style.display='none'; document.getElementById('Code_Open_Image_99342').style.display='inline'; document.getElementById('Code_Open_Text_99342').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_99342" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_99342').style.display='none'; getElementById('Code_Closed_Image_99342').style.display='inline'; getElementById('Code_Closed_Text_99342').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_99342" class="cnblogs_code_Collapse"&gt;code&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_99342"&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; void registerBut_Click(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;br/&gt;{&lt;br/&gt;    if (this.sAnswerCode(this.requestCode.Text) == this.answerCode.Text)&lt;br/&gt;    {&lt;br/&gt;        this.setRegStr(this.answerCode.Text);&lt;br/&gt;        MessageBox.Show("&lt;span style="color: #8b0000"&gt;注册成功。&lt;/span&gt;");&lt;br/&gt;    }&lt;br/&gt;    else&lt;br/&gt;    {&lt;br/&gt;        MessageBox.Show("&lt;span style="color: #8b0000"&gt;注册码错误！&lt;/span&gt;");&lt;br/&gt;    }&lt;br/&gt;}&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;显然，&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://KJSNetSet:1.0.0.0/KJSNetSet.SystemSet/requestCode:System.Windows.Forms.TextBox"&gt;requestCode&lt;/a&gt;就是申请码那个TextBox，&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://KJSNetSet:1.0.0.0/KJSNetSet.SystemSet/answerCode:System.Windows.Forms.TextBox"&gt;answerCode&lt;/a&gt;就是注册码那个TextBox，完成找ID号的工作。 &lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; &lt;p&gt;然后去找为&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://KJSNetSet:1.0.0.0/KJSNetSet.SystemSet/requestCode:System.Windows.Forms.TextBox"&gt;requestCode&lt;/a&gt;赋值的语句： &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_380972" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_380972').style.display='none'; document.getElementById('Code_Open_Image_380972').style.display='inline'; document.getElementById('Code_Open_Text_380972').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_380972" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_380972').style.display='none'; getElementById('Code_Closed_Image_380972').style.display='inline'; getElementById('Code_Closed_Text_380972').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_380972" class="cnblogs_code_Collapse"&gt;code&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_380972"&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Main()&lt;br/&gt;{&lt;br/&gt;    Application.EnableVisualStyles();&lt;br/&gt;    Application.SetCompatibleTextRenderingDefault(&lt;span style="color: #0000ff"&gt;false&lt;/span&gt;);&lt;br/&gt;    Application.Run(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SystemSet());&lt;br/&gt;}&lt;br/&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; SystemSet()&lt;br/&gt;{&lt;br/&gt;   ………&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.requestCode.Text = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.CpuID();&lt;br/&gt;   ………&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; CpuID()&lt;br/&gt;{&lt;br/&gt;   &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; text2 = "&lt;span style="color: #8b0000"&gt;&lt;/span&gt;";&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;try&lt;/span&gt;&lt;br/&gt;         {&lt;br/&gt;         ManagementObjectSearcher managementObjectSearcher = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ManagementObjectSearcher("&lt;span style="color: #8b0000"&gt;SELECT * FROM Win32_Processor&lt;/span&gt;");&lt;br/&gt;         ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectCollection.GetEnumerator())&lt;br/&gt;            {&lt;br/&gt;               .....&lt;br/&gt;            }&lt;br/&gt;               .....&lt;br/&gt;          }&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;catch&lt;/span&gt;&lt;br/&gt;          {&lt;br/&gt;                &lt;br/&gt;          }&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; text2;&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;/span&gt;可以看出来，是CpuID方法用于产生申请码的，应该是根据cpu等硬件信息产生的，这就可以为每台计算机产生不同的申请码。最终经过处理返回text2就是申请码，用try-catch代码块，把所有可能错误都忽略了，难怪没申请码但是也没报错~~~&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt;&lt;/p&gt;将CpuID这个方法在我本机的VS中执行了一下（需要引用System.Management），果然报错了，查看catch抛出的异常，是在调用&lt;a&gt;&lt;strong&gt;searcher&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;strong&gt;Get&lt;/strong&gt;&lt;strong&gt;()&lt;/strong&gt;时抛出的异常，信息如下：&amp;nbsp;{System.Runtime.InteropServices.COMException (0x80070422): &lt;strong&gt;无法启动服务&lt;/strong&gt;，原因可能是已被禁用或与其相关联的设备没有启动。 (异常来自 HRESULT:0x80070422)&lt;br&gt;&amp;nbsp;&amp;nbsp; 在 System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)&lt;br&gt;&amp;nbsp;&amp;nbsp; 在 System.Management.ManagementScope.InitializeGuts(Object o)&lt;br&gt;&amp;nbsp;&amp;nbsp; 在 System.Management.ManagementScope.Initialize()&lt;br&gt;&amp;nbsp;&amp;nbsp; 在 System.Management.ManagementObjectSearcher.Initialize()&lt;br&gt;&amp;nbsp;&amp;nbsp; 在 System.Management.ManagementObjectSearcher.Get()&lt;p&gt;可能我机子的某个服务没有启动造成的，而Get方法却使用了此服务。Get的智能提示是：“异步调用 &lt;strong&gt;WMI&lt;/strong&gt; 查询，并绑定到一个观察程序以传递结果。”这就明白了，去服务里看了一下，我的Windows Management Instrumentation确实没有启动，这是我之前优化系统时关上的，没想到在这里用到了。启动此服务后，程序运行正常了，可以产生申请码。&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt;&lt;/p&gt;虽然我知道了是这个问题，不过以后要是再使用这个软件的机子发生同样问题呢？总不能还配一个专门的说明书，所以如果能给打个小小的补丁，那是最好。不过因为没有源码，原则上是&lt;strong&gt;修改越少越好&lt;/strong&gt;，而不是一味追求完美的修补。为了方便起见，我这里只想在CPUID方法的catch中加一个提示语句：MessageBox.Show("无法生成申请码，请确认系统WMI服务启动正常！");&lt;br/&gt;这肯定是不够严谨的，不会所有的异常都是因为WMI未启动导致的，因为修改IL比较麻烦，这里就为了最小化修改的原则，以后发现别的问题再说。操作思路如下：&lt;ul&gt;&lt;li&gt;得到程序的IL代码； &lt;li&gt;将需要的提示信息的IL代码注入； &lt;li&gt;反编译程序； &lt;li&gt;测试程序运行是否正常。&amp;nbsp; &lt;/li&gt;&lt;/ul&gt;&lt;strong&gt;(1)&lt;/strong&gt;首先，利用&lt;a href="http://msdn.microsoft.com/zh-cn/library/f7dy01k1.aspx"&gt;ILDASM&lt;/a&gt;(.NET的IL反汇编程序，Framework自带的)，打开这个程序，然后&lt;strong&gt;转储&lt;/strong&gt;它的IL及其相关文件(这里我转储为programIL，一共生成4个文件，一个IL后缀，两个resources后缀，还一个res后缀)。&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910305397.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="1" border="0" alt="1" src="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910301776.jpg" width="296" height="436"&gt;&lt;/a&gt; &lt;strong&gt;(2)&lt;/strong&gt;将其中以IL后缀名的文件用文本编辑器打开，并找到CpuID的IL代码段，可以看到如下：&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_236283" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_236283').style.display='none'; document.getElementById('Code_Open_Image_236283').style.display='inline'; document.getElementById('Code_Open_Text_236283').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_236283" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_236283').style.display='none'; getElementById('Code_Closed_Image_236283').style.display='inline'; getElementById('Code_Closed_Text_236283').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_236283" class="cnblogs_code_Collapse"&gt;code&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_236283"&gt;.&lt;span style="color: #0000ff"&gt;method&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;hidebysig&lt;/span&gt; &lt;span style="color: #0000ff"&gt;instance&lt;/span&gt; string &lt;br/&gt;          CpuID() &lt;span style="color: #0000ff"&gt;cil&lt;/span&gt; &lt;span style="color: #0000ff"&gt;managed&lt;/span&gt;&lt;br/&gt;{&lt;br/&gt;......省略&lt;br/&gt;&lt;span style="color: #0000ff"&gt;catch&lt;/span&gt; [mscorlib]System.Object &lt;br/&gt;    {&lt;br/&gt;      IL_00b2:  &lt;span style="color: #0000ff"&gt;pop&lt;/span&gt;&lt;br/&gt;      IL_00b3:  &lt;span style="color: #0000ff"&gt;leave&lt;/span&gt;.s    IL_00b5 &lt;br/&gt;&lt;br/&gt;    }  &lt;span style="color: #008000"&gt;// end handler&lt;/span&gt;&lt;br/&gt;    IL_00b5:  &lt;span style="color: #0000ff"&gt;ldloc&lt;/span&gt;.1&lt;br/&gt;    IL_00b6:  &lt;span style="color: #0000ff"&gt;ret&lt;/span&gt;&lt;br/&gt;} &lt;span style="color: #008000"&gt;// end of method SystemSet::CpuID&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;pre&gt;&amp;nbsp;可以看到IL的catch中，确实什么都没干，就直接跳转出去了。我这里需要在里面加上上面说的提示信息，修改完毕的代码是：&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_289969" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_289969').style.display='none'; document.getElementById('Code_Open_Image_289969').style.display='inline'; document.getElementById('Code_Open_Text_289969').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_289969" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_289969').style.display='none'; getElementById('Code_Closed_Image_289969').style.display='inline'; getElementById('Code_Closed_Text_289969').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_289969" class="cnblogs_code_Collapse"&gt;code&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_289969"&gt;&lt;span style="color: #0000ff"&gt;method&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;hidebysig&lt;/span&gt; &lt;span style="color: #0000ff"&gt;instance&lt;/span&gt; string &lt;br/&gt;          CpuID() &lt;span style="color: #0000ff"&gt;cil&lt;/span&gt; &lt;span style="color: #0000ff"&gt;managed&lt;/span&gt;&lt;br/&gt;{&lt;br/&gt;......省略&lt;br/&gt;    &lt;span style="color: #0000ff"&gt;catch&lt;/span&gt; [mscorlib]System.Object &lt;br/&gt;    {&lt;br/&gt;      IL_00b2:  &lt;span style="color: #0000ff"&gt;pop&lt;/span&gt;&lt;br/&gt;      IL_00b3:  &lt;span style="color: #0000ff"&gt;nop&lt;/span&gt;&lt;br/&gt;  &lt;br/&gt;      IL_00b4:  &lt;span style="color: #0000ff"&gt;ldstr&lt;/span&gt;      “无法生成申请码，请确认系统WMI服务启动正常！"   &lt;br/&gt;      IL_00b9:  &lt;span style="color: #0000ff"&gt;call&lt;/span&gt;       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)&lt;br/&gt;      IL_00be:  &lt;span style="color: #0000ff"&gt;pop&lt;/span&gt;&lt;br/&gt;      IL_00c0:  &lt;span style="color: #0000ff"&gt;nop&lt;/span&gt;&lt;br/&gt;      IL_00c1:  &lt;span style="color: #0000ff"&gt;nop&lt;/span&gt;&lt;br/&gt;      IL_00c2:  &lt;span style="color: #0000ff"&gt;nop&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;      IL_00c3:  &lt;span style="color: #0000ff"&gt;leave&lt;/span&gt;.s    IL_00c5&lt;br/&gt;&lt;br/&gt;    }  &lt;span style="color: #008000"&gt;// end handler&lt;/span&gt;&lt;br/&gt;      IL_00c5:  &lt;span style="color: #0000ff"&gt;ldloc&lt;/span&gt;.1&lt;br/&gt;      IL_00c6:  &lt;span style="color: #0000ff"&gt;ret&lt;/span&gt;&lt;br/&gt;} &lt;span style="color: #008000"&gt;// end of method SystemSet::CpuID&lt;/span&gt;&lt;br/&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&amp;nbsp;对加入的IL稍做说明：&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;IL_00b4位置的&lt;span style="color: #0000ff"&gt;ldstr&lt;/span&gt; ，意思就是将后面的字符串的对象引用推送到堆栈上； &lt;li&gt;然后IL_00b9的&lt;font color="#0000ff"&gt;call&lt;/font&gt;就是调用了MessageBox.Show方法，当然，上面的字符串就会被当参数传入； &lt;li&gt;最后IL_00be处执行&lt;font color="#0000ff"&gt;pop&lt;/font&gt;，将位于顶部的刚才压入的字符串引用从堆栈中弹出；&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;经过上面压栈，调用，出栈，我们的自定义代码注入进去了，并且又恢复了修改前栈的状态，因此不影响栈的平衡。这里想强调一下，上面的IL写法不是唯一的，比如我们完全可以将那个字符串“无法生成申请码，请确认系统WMI服务启动正常！”用如下代码来代替：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff"&gt;bytearray&lt;/span&gt; (E0 65 D5 6C 1F 75 10 62 33 75 F7 8B 01 78 0C FF F7 8B 6E 78 A4 8B FB 7C DF 7E 57 00 4D 00 49 00 0D 67 A1 52 2F 54 A8 52 63 6B 38 5E 01 FF )&lt;/p&gt;&lt;p&gt;其中bytearray里面的代码为汉字的unicode码。&lt;/p&gt;&lt;p&gt;还需要注意的是：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;为了计算代码方便，插入了一些nop命令，它用于在修补操作码的情况下填充空间； &lt;li&gt;由于插入了一些代码，因此IL的地址需要变化，插入代码后的源IL代码的地址应该后推，也就是之前是到IL_00b3就跳出catch，现在变成到了IL_00c3才退出，相差了16（16进制）； &lt;li&gt;因为从IL_00b3开始到最后所有的代码的地址都变化了，如果在之前有代码跳转到这些代码中的话，需要也相应修改！这个程序就有一个地方，就是在try没有报错情况下，他会跳过catch段而直接到原来的IL_00c5（leave.s&amp;nbsp;&amp;nbsp;&amp;nbsp; IL_00b5），因此我将其改为了IL_00c5即可。&lt;/li&gt;&lt;/ul&gt;&lt;strong&gt;(3)&lt;/strong&gt;利用&lt;a href="http://msdn.microsoft.com/zh-cn/library/496e4ekx.aspx"&gt;Ilasm&lt;/a&gt;（(.NET的IL汇编程序，Framework自带的），将修改好的IL汇编成为应用程序。这个只提供了命令行版本的。以下是命令，将这个il编译到newProgram.exe，还有许多参数，可以参照ilasm的连接；&lt;strong&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201108/20110822091030663.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="1" border="0" alt="1" src="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910301187.jpg" width="550" height="65"&gt;&lt;/a&gt; &lt;/strong&gt;执行过程略去，以下是部分统计信息：&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910321054.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="2" border="0" alt="2" src="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910327924.jpg" width="554" height="376"&gt;&lt;/a&gt; 可以看出，没有出现任何错误，提示编译成功。&amp;nbsp;&lt;strong&gt;(4)&lt;/strong&gt;运行一下新程序，当WMI服务停止时，确实提示了信息：&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910338447.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="3" border="0" alt="3" src="http://images.cnblogs.com/cnblogs_com/lerit/201108/201108220910434010.jpg" width="299" height="145"&gt;&lt;/a&gt; 经过其他测试，没有影响之前程序的功能，修改就算成功了。&amp;nbsp;&lt;strong&gt;总结：&lt;/strong&gt;&lt;ol&gt;&lt;li&gt;注册机作为保护别的软件的一种程序，如果自己都保护不好，还谈何保护其他软件呢，起码的反编译措施还是需要做一下的； &lt;li&gt;try-catch应该是解决软件异常而建立的机制，而不是忽略问题和省事儿的做法，最起码为了用户不至于一头雾水，为了软件发现bug后能够有效反馈和更新，给点儿提示或做个日志也是好的； &lt;li&gt;编码和测试都有很大欠缺，尤其是和系统环境相关的代码，一方面编码应该给出一些建设性的建议（比如WMI服务没有启动，请启动服务等），另一方面，测试的环境也不应该仅仅限于开发机器； &lt;li&gt;微软的IL中间语言还是很强大的，入门起来比汇编容易，当然深入了，也比较难；&lt;li&gt;系统服务不要乱优化~； &lt;li&gt;事后诸葛亮，也许我也会犯同样的问题，用别人的错误提高自己吧：）&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;参考：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://msdn.microsoft.com/zh-cn/library/496e4ekx%28v=VS.80%29.aspx"&gt;&lt;font size="2"&gt;MSIL 汇编程序&lt;/font&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://msdn.microsoft.com/zh-cn/library/f7dy01k1%28VS.80%29.aspx"&gt;&lt;font size="2"&gt;MSIL 反汇编程序&lt;/font&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://msdn.microsoft.com/zh-cn/library/812xyxy2.aspx"&gt;&lt;font size="2"&gt;MSIL OpCodes 字段&lt;/font&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2148858.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/08/22/2148858.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/08/18/2144787.html</id><title type="text">GridView激发了未处理的事件&amp;ldquo;PageIndexChanging&amp;rdquo;的分析</title><summary type="text">这个问题网上有许多解决方法，主要是因为没有为GridView指定PageIndexChanging事件： 解决方法： 首先在aspx中指定： &lt;asp:GridView ID="GridView2" runat="server" OnPageIndexChanging="GridView1_PageIndexChanging" AllowPaging="True" PageSize="10"&gt; &lt;...</summary><published>2011-08-18T08:38:00Z</published><updated>2011-08-18T08:38:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/08/18/2144787.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/08/18/2144787.html"/><content type="html">&lt;p&gt;这个问题网上有许多解决方法，主要是因为没有为GridView指定PageIndexChanging事件：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解决方法：&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先在aspx中指定：&lt;/p&gt;        &amp;lt;asp:GridView ID="GridView2" runat="server" OnPageIndexChanging="GridView1_PageIndexChanging"&lt;br/&gt;            AllowPaging="True" PageSize="10"&amp;gt;&lt;br/&gt;        &amp;lt;/asp:GridView&amp;gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;div&gt;然后后台代码：&lt;/div&gt;protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)&lt;br/&gt;    {&lt;br/&gt;        GridView1.PageIndex = e.NewPageIndex;&lt;br/&gt;        GridView1.DataSource = getData();//调用绑定的具体方法&lt;br/&gt;         GridView1.DataBind();&lt;br/&gt;    }&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;通过这个设置，是可以解决标题所示报错的。&lt;/p&gt;&lt;p&gt;之所以把这个问题拿出来说一下，主要是我之前也用过GridView，记得那时候也是可以分页的，但是并没有使用PageIndexChanging事件来处理也没问题啊。经过网上文章总结，发现是GridView&lt;strong&gt;数据源绑定方式&lt;/strong&gt;的问题：&lt;/p&gt;&lt;p&gt;如果对GridView使用了DataSource来&lt;strong&gt;手动绑定&lt;/strong&gt;，就必须来手动处理翻页。手动绑定就是设置GridView的DataSource（比如一个DataTable 等）并调用它的DataBind方法来实现绑定。（就是上面的方法）&lt;/p&gt;&lt;p&gt;而我之前之所以没有遇到过这个错误，是因为使用了&lt;strong&gt;自动绑定，&lt;/strong&gt;也就是通过设置GridView的DataSourceID，将其指定给某一个数据源控件（比如ObjectDataSource,SqlDataSource等），因为这些控件具有一些自动处理表格的功能，因此就无省去了许多手工编码工作。&lt;/p&gt; &amp;lt;asp:GridView ID="GridView1" runat="server" AllowPaging="True" AutoGenerateColumns="False"&lt;br/&gt;            DataSourceID="SqlDataSource1" PageSize="10"&amp;gt;&lt;br/&gt;            &amp;lt;Columns&amp;gt;&lt;br/&gt;                &amp;lt;asp:BoundField DataField="ID" HeaderText="ID" /&amp;gt;&lt;br/&gt;                &amp;lt;asp:BoundField DataField="Name" HeaderText="Name" /&amp;gt;&lt;br/&gt;            &amp;lt;/Columns&amp;gt;&lt;br/&gt;        &amp;lt;/asp:GridView&amp;gt;&lt;br/&gt;        &amp;lt;asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="&amp;lt;%$ ConnectionStrings:MyConnectionString %&amp;gt;"&lt;br/&gt;            SelectCommand="SELECT * FROM [table1]"&amp;gt;&amp;lt;/asp:SqlDataSource&amp;gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;现在很少用控件了，不是因为不好用，而是工作中一直搞前端开发，所以忘得差不多了。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2144787.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/08/18/2144787.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html</id><title type="text">ElMAH（ASP.NET错误日志记录与通知）系列文章-基础应用篇</title><summary type="text">最近在研究几个ASP.NET的开源项目时，发现都在使用ELMAH来作为记录整个应用程序的错误日志，于是拿来小研究了一下，在这里和各位分享一下，如果有使用过的，请多指教。 对于ELMAH，将用三篇系列文章来介绍： 概念篇 基本应用篇 高级应用篇 基本应用篇 1.ELMAH初体验 先来快速体验一下ELMAH应用于一个项目的最少操作是多么简单吧。（以开发环境下的ASP.NET程序为例） 步骤1：用VS...</summary><published>2011-04-19T02:17:00Z</published><updated>2011-04-19T02:17:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 最近在研究几个ASP.NET的开源项目时，发现都在使用ELMAH来作为记录整个应用程序的错误日志，于是拿来小研究了一下，在这里和各位分享一下，如果有使用过的，请多指教。  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于ELMAH，将用三篇系列文章来介绍：  &lt;ul&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html"&gt;概念篇&lt;/a&gt;  &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html"&gt;&lt;u&gt;基本应用篇&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;  &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 高级应用篇&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font size="5"&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp; 基本应用篇&lt;/strong&gt;&lt;/font&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;1.ELMAH初体验&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;先来快速体验一下ELMAH应用于一个项目的最少操作是多么简单吧。（以开发环境下的ASP.NET程序为例）&lt;/p&gt; &lt;p&gt;&lt;strong&gt;步骤1：&lt;/strong&gt;用VS2010开发平台打开需要添加ELMAH程序的ASP.NET项目。使用NuGet（一个用于维护.NET FrameWork常用类库的VS2010扩展，当然ELMAH也在其中，NuGet的介绍及其安装请看&lt;strong&gt;参考文章1&lt;/strong&gt;），添加ELMAH。即：&lt;/p&gt; &lt;p&gt;菜单选择：工具-Library Package Manager-Add Library Package Reference…，打开添加类库界面。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016273813.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="op1" border="0" alt="op1" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016272701.jpg" width="512" height="364"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;在线搜索ELMAH，点击install按钮，则ELMAH的配置工作自动完成。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016279079.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="op2" border="0" alt="op2" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016278731.jpg" width="522" height="349"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;步骤2：&lt;/strong&gt;其实上面一个步骤，就完成了所有配置了。现在，来试试效果吧：&lt;/p&gt; &lt;p&gt;首先制造一个报错。任选一个页面（这里假设为index.aspx），我在Page_Onload事件中加了一句：&lt;/p&gt; &lt;div align="left"&gt; protected void Page_Load(object sender, EventArgs e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int a = Convert.ToInt16("w");&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/div&gt;&lt;p&gt;显然在加载这个页面时，会报错的。运行此页面（这里地址是&lt;u&gt;&lt;a href="http://localhost:52450/MyTestWebProject/Default.aspx"&gt;http://localhost:52450/MyTestWebProject/Default.aspx&lt;/a&gt;&lt;/u&gt;），如果系统没有别的配置，应该看到我们最常见的黄颜色报错界面。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016289570.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="error1" border="0" alt="error1" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016285600.jpg" width="462" height="412"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;现在来看看ELMAH记录这个错误的情况，输入：&lt;u&gt;&lt;a href="http://localhost:52450/MyTestWebProject/elmah.axd"&gt;http://localhost:52450/MyTestWebProject/&lt;strong&gt;elmah.axd&lt;/strong&gt;&lt;/a&gt;&lt;/u&gt;即可看到记录下的错误信息。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016282535.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="error2" border="0" alt="error2" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016295011.jpg" width="780" height="198"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;以上操作也可以参考&lt;a href="http://www.cnblogs.com/liping13599168/archive/2011/02/23/1962625.html"&gt;liping13599168&lt;/a&gt;老兄的文章，图文并茂，比较详细。&lt;/p&gt;&lt;p&gt;就是这么简单，ELMAH在后台记录了错误信息，并为我们提供了查询错误日志信息的界面，只需要简单的操作，就完成了基本的需求。当然，ELMAH还提供了丰富的功能，从而满足不同的需求，之后将会介绍。&lt;/p&gt;&lt;p&gt;上述使用NuGet来配置ELMAH，对用户来说是透明的，下面来看看他具体都干了写什么：&lt;/p&gt;&lt;p&gt;（1）将Elmah.dll复制到程序的根目录的Bin文件夹下（若没有自动创建）；&lt;/p&gt;&lt;p&gt;（2）在web.config中configuration、httpHandlers和httpModules中增加了如下内容（斜体部分）：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_299092" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_299092').style.display='none'; document.getElementById('Code_Open_Image_299092').style.display='inline'; document.getElementById('Code_Open_Text_299092').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_299092" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_299092').style.display='none'; getElementById('Code_Closed_Image_299092').style.display='inline'; getElementById('Code_Closed_Text_299092').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_299092" class="cnblogs_code_Collapse"&gt;web.config&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_299092"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configuration&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;em&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"security"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.SecuritySectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorMail"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorMailSectionHandler, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorFilter"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorFilterSectionHandler, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/em&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;httpHandlers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;em&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add&lt;/span&gt; &lt;span style="color: #ff0000"&gt;verb&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"POST,GET,HEAD"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;path&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah.axd"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogPageFactory, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/em&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;httpHandlers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;   &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;httpModules&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;em&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"ErrorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogModule, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/em&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;httpModules&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_412230"&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;就这些，他已经可以实现ELMAH的基本功能了。因此，如果对于不想使用NuGet自动配置，或者在生产环境下已经运行的程序来说，按照如上两个步骤，依然可以手动配置。&lt;/p&gt;&lt;p&gt;现在来回顾一下上一篇文章吧，结合这个例子，从而更形象的理解它的工作机制。&lt;/p&gt;&lt;p&gt;当请求的页面报错后，就如上面所示，将会把那个报错的黄颜色界面返回给用户，在返回时，先被httpModules中的名为ErrorLog的模块截获，这一模块将错误信息记录起来；当需要查看日志信息时（如上所示访问根目录下elmah.axd文件），这个请求发给IIS后，先被httpHandlers捕获，并交给了其中专门处理elmah.axd的处理程序，这个处理程序就把那个错误日志界面展示给用户了。&lt;/p&gt;&lt;p&gt;至于web.config中新增加的&amp;lt;sectionGroup name="elmah"&amp;gt; 以及其中的信息，主要是实现更多丰富功能的配置项，这将会是本文余下部分以及下一章的内容。这里只简单说一下，以便有个印象：（实际上NuGet还少加了一个，这里给出完整版）&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_420349" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_420349').style.display='none'; document.getElementById('Code_Open_Image_420349').style.display='inline'; document.getElementById('Code_Open_Text_420349').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_420349" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_420349').style.display='none'; getElementById('Code_Closed_Image_420349').style.display='inline'; getElementById('Code_Closed_Text_420349').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_420349" class="cnblogs_code_Collapse"&gt;web.config&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_420349"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"security"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.SecuritySectionHandler, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorMail"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorMailSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorFilter"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorFilterSectionHandler, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorTweet"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorTweetSectionHandler, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt; &lt;/div&gt;&lt;/span&gt;&amp;nbsp;&lt;p&gt;其中security用于配置访问日志信息的权限，errorLog用于配置记录日志的存储方式（数据库、文件还是内存等），errorMail和errorTweet分别用于配置发生错误时，以邮件或Twitter方式通知管理员的操作，errorFilter用于配置记录日志时，过滤错误信息的类型。由于每一个都对应一个功能，因此可以根据需要只写其中的几个也是没问题的。另外，每一条都类似于一个声明，告诉ELMAH，用户要实现某几个功能，至于如何实现，那么还需要再web.config的其他地方告诉具体的实现方式，下面将例子时候就清楚了。本章下面只讲errorLog，其他内容见下一篇。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.配置存储方式&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;有人可能会问，上面的自动配置中，并没有指定存储日志的方式啊（当然这里还没介绍如何配置，但是从上面配置中，似乎也看不到有哪里指定了存储方式），那这些数据存储在哪里了呢？答案是，NuGet安装ELMAH后，它是没有指定任何存储方式。而ELMAH认为，如果没有指定存储方式，那么就采用默认的&lt;strong&gt;内存存储方式（也可以显式的指定）&lt;/strong&gt;。但是这种存储方式只能作为调试阶段使用，生产环境下不应使用此方式，具体的缺点请看下面对内存存储方式的介绍。&lt;/p&gt;&lt;p&gt;接下来就具体介绍各种存储方式，分别以数据库存储、文件存储和内存存储为例，需要强调一点，ELMAH目前只支持一下三种方式中的任意一种，不支持同时采用多种记录方式。（想必也没这个必要）&lt;/p&gt;&lt;p&gt;（1）数据库存储方式&lt;/p&gt;&lt;p&gt;即将发布的v1.2正式版本的ELMAH支持的数据库有：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.microsoft.com/sql/"&gt;Microsoft SQL Server&lt;/a&gt; &lt;li&gt;&lt;a href="http://www.oracle.com/"&gt;Oracle&lt;/a&gt; (&lt;a href="http://elmah.googlecode.com/svn/tags/REL-1.1/src/Elmah/OracleErrorLog.cs"&gt;OracleErrorLog&lt;/a&gt;) &lt;li&gt;&lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; (version 3) database file &lt;li&gt;&lt;a href="http://www.vistadb.net/"&gt;VistaDB&lt;/a&gt; (&lt;a href="http://elmah.googlecode.com/svn/tags/REL-1.1/src/Elmah/VistaDBErrorLog.cs"&gt;VistaDBErrorLog&lt;/a&gt;); 在1.2版，不再推荐使用 &lt;li&gt;&lt;a href="http://code.google.com/p/elmah/wiki/Access"&gt;Microsoft Access&lt;/a&gt; (&lt;a href="http://elmah.googlecode.com/svn/tags/REL-1.1/src/Elmah/AccessErrorLog.cs"&gt;AccessErrorLog&lt;/a&gt;) &lt;li&gt;&lt;a href="http://www.microsoft.com/sql/"&gt;SQL Server Compact Edition&lt;/a&gt; (1.2支持) &lt;li&gt;&lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; (1.2支持) &lt;li&gt;&lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; (1.2支持)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;要以数据库作为日志的存储方式，应该完成三件事：&lt;/p&gt;&lt;p&gt;a) 告诉ELMAH使用哪种数据库作为存储数据库;&lt;/p&gt;&lt;p&gt;b) 告诉ELMAH如何连接到数据库;&lt;/p&gt;&lt;p&gt;c) 指定的数据库里，要包含ELMAH需要的表、视图和存储过程等（嵌入式数据库不需要此过程）。&lt;/p&gt;&lt;p&gt;其中a和b步骤需要在web.config中指定，c则需要在数据库中添加相关对象。&lt;/p&gt;&lt;p&gt;这里以SQL Server数据库为例，其它数据库的配置类似，不再赘述。&lt;/p&gt;&lt;p&gt;web.config配置如下（httpModules以及httpHandlers就不贴了，这里只给出ELMAH记录日志于sqlserver数据库的配置）：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_746365" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_746365').style.display='none'; document.getElementById('Code_Open_Image_746365').style.display='inline'; document.getElementById('Code_Open_Text_746365').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_746365" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_746365').style.display='none'; getElementById('Code_Closed_Image_746365').style.display='inline'; getElementById('Code_Closed_Text_746365').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_746365" class="cnblogs_code_Collapse"&gt;web.config&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_746365"&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;  &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--告诉elmah，我要记录错误日志--&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;   &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #008000"&gt;&amp;lt;!-- 告诉elmah，我要采用sqlserver来记录我的日志，连接那个数据库的字符串名为myconnectionString。--&amp;gt;&lt;/span&gt;&lt;br/&gt;   &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;errorLog&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.SqlErrorLog, Elmah"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;connectionStringName&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"myconnectionString"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--&lt;br/&gt;实际上，当使用asp.net 2.0及以上版本时，上面的这种方式是没问题的，如果使用asp.net 1.x版本，应该使用：&lt;br/&gt;&amp;lt;errorLog type="Elmah.SqlErrorLog, Elmah" connectionString="Data Source=.;Initial Catalog=MVCMUSICSTORE;Integrated Security=True" /&amp;gt; &lt;br/&gt;也就是说，这种方式把下面那个连接字符串直接写在这里了，就不需要下面的那个配置了。&lt;br/&gt;--&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;connectionStrings&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #008000"&gt;&amp;lt;!-- 这就是我们用于连接sqlserver数据库的连接字符串。--&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"myconnectionString"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;connectionString&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Data Source=.;Initial Catalog=MVCMUSICSTORE;Integrated Security=True"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;connectionStrings&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;没什么好说，都注释清楚了，这一配置告诉了采用哪种数据库来记录，以及如何连接数据库。&lt;/p&gt;&lt;p&gt;再来看看如何实现c过程，也就是创建一些对象。数据库要能记录日志，一定需要一些表的，当然可能还有辅助的视图、存储过程等。可惜，ELMAH不能自动创建这些对象，我们需要手动添加。只需要在数据库中执行如下SQL语句即可：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_492997" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_492997').style.display='none'; document.getElementById('Code_Open_Image_492997').style.display='inline'; document.getElementById('Code_Open_Text_492997').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_492997" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_492997').style.display='none'; getElementById('Code_Closed_Image_492997').style.display='inline'; getElementById('Code_Closed_Text_492997').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_492997" class="cnblogs_code_Collapse"&gt;sql&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_492997"&gt;CREATE TABLE dbo.ELMAH_Error&lt;br/&gt;(&lt;br/&gt;    ErrorId     UNIQUEIDENTIFIER NOT NULL,&lt;br/&gt;    Application NVARCHAR(60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    Host        NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    Type        NVARCHAR(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    Source      NVARCHAR(60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    Message     NVARCHAR(500) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    [User]      NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,&lt;br/&gt;    StatusCode  INT NOT NULL,&lt;br/&gt;    TimeUtc     DATETIME NOT NULL,&lt;br/&gt;    Sequence    INT IDENTITY (1, 1) NOT NULL,&lt;br/&gt;    AllXml      NTEXT COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL &lt;br/&gt;) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]&lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;ALTER TABLE dbo.ELMAH_Error WITH NOCHECK ADD &lt;br/&gt;    CONSTRAINT PK_ELMAH_Error PRIMARY KEY NONCLUSTERED&lt;br/&gt;    (&lt;br/&gt;        ErrorId&lt;br/&gt;    )  ON [PRIMARY] &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;ALTER TABLE dbo.ELMAH_Error ADD &lt;br/&gt;    CONSTRAINT DF_ELMAH_Error_ErrorId DEFAULT (newid()) FOR [ErrorId]&lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;CREATE NONCLUSTERED INDEX IX_ELMAH_Error_App_Time_Seq ON dbo.ELMAH_Error&lt;br/&gt;(&lt;br/&gt;    [Application] ASC,&lt;br/&gt;    [TimeUtc] DESC,&lt;br/&gt;    [Sequence] DESC&lt;br/&gt;) ON [PRIMARY]&lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;SET QUOTED_IDENTIFIER ON &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;CREATE PROCEDURE dbo.ELMAH_GetErrorXml&lt;br/&gt;(&lt;br/&gt;    @Application NVARCHAR(60),&lt;br/&gt;    @ErrorId UNIQUEIDENTIFIER&lt;br/&gt;)&lt;br/&gt;AS&lt;br/&gt;&lt;br/&gt;SET NOCOUNT ON&lt;br/&gt;&lt;br/&gt;SELECT &lt;br/&gt;    AllXml&lt;br/&gt;FROM &lt;br/&gt;    ELMAH_Error&lt;br/&gt;WHERE&lt;br/&gt;    ErrorId = @ErrorId&lt;br/&gt;AND&lt;br/&gt;    Application = @Application&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;GO&lt;br/&gt;SET QUOTED_IDENTIFIER OFF &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;SET QUOTED_IDENTIFIER ON &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;CREATE PROCEDURE dbo.ELMAH_GetErrorsXml&lt;br/&gt;(&lt;br/&gt;    @Application NVARCHAR(60),&lt;br/&gt;    @PageIndex INT = 0,&lt;br/&gt;    @PageSize INT = 15,&lt;br/&gt;    @TotalCount INT OUTPUT&lt;br/&gt;)&lt;br/&gt;AS &lt;br/&gt;&lt;br/&gt;SET NOCOUNT ON&lt;br/&gt;&lt;br/&gt;DECLARE @FirstTimeUTC DateTime&lt;br/&gt;DECLARE @FirstSequence int&lt;br/&gt;DECLARE @StartRow int&lt;br/&gt;DECLARE @StartRowIndex int&lt;br/&gt;&lt;br/&gt;-- Get the ID of the first error for the requested page&lt;br/&gt;&lt;br/&gt;SET @StartRowIndex = @PageIndex * @PageSize + 1&lt;br/&gt;SET ROWCOUNT @StartRowIndex&lt;br/&gt;&lt;br/&gt;SELECT  &lt;br/&gt;    @FirstTimeUTC = TimeUTC,&lt;br/&gt;    @FirstSequence = Sequence&lt;br/&gt;FROM &lt;br/&gt;    ELMAH_Error&lt;br/&gt;WHERE   &lt;br/&gt;    Application = @Application&lt;br/&gt;ORDER BY &lt;br/&gt;    TimeUTC DESC, &lt;br/&gt;    Sequence DESC&lt;br/&gt;&lt;br/&gt;-- Now set the row count to the requested page size and get&lt;br/&gt;-- all records below it for the pertaining application.&lt;br/&gt;&lt;br/&gt;SET ROWCOUNT @PageSize&lt;br/&gt;&lt;br/&gt;SELECT &lt;br/&gt;    @TotalCount = COUNT(1) &lt;br/&gt;FROM &lt;br/&gt;    ELMAH_Error&lt;br/&gt;WHERE &lt;br/&gt;    Application = @Application&lt;br/&gt;&lt;br/&gt;SELECT &lt;br/&gt;    errorId, &lt;br/&gt;    application,&lt;br/&gt;    host, &lt;br/&gt;    type,&lt;br/&gt;    source,&lt;br/&gt;    message,&lt;br/&gt;    [user],&lt;br/&gt;    statusCode, &lt;br/&gt;    CONVERT(VARCHAR(50), TimeUtc, 126) + 'Z' time&lt;br/&gt;FROM &lt;br/&gt;    ELMAH_Error error&lt;br/&gt;WHERE&lt;br/&gt;    Application = @Application&lt;br/&gt;AND &lt;br/&gt;    TimeUTC &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;= @FirstTimeUTC&lt;br/&gt;AND &lt;br/&gt;    Sequence &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;= @FirstSequence&lt;br/&gt;ORDER BY&lt;br/&gt;    TimeUTC DESC, &lt;br/&gt;    Sequence DESC&lt;br/&gt;FOR&lt;br/&gt;    XML AUTO&lt;br/&gt;&lt;br/&gt;GO&lt;br/&gt;SET QUOTED_IDENTIFIER OFF &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;SET QUOTED_IDENTIFIER ON &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;br/&gt;&lt;br/&gt;CREATE PROCEDURE dbo.ELMAH_LogError&lt;br/&gt;(&lt;br/&gt;    @ErrorId UNIQUEIDENTIFIER,&lt;br/&gt;    @Application NVARCHAR(60),&lt;br/&gt;    @Host NVARCHAR(30),&lt;br/&gt;    @Type NVARCHAR(100),&lt;br/&gt;    @Source NVARCHAR(60),&lt;br/&gt;    @Message NVARCHAR(500),&lt;br/&gt;    @User NVARCHAR(50),&lt;br/&gt;    @AllXml NTEXT,&lt;br/&gt;    @StatusCode INT,&lt;br/&gt;    @TimeUtc DATETIME&lt;br/&gt;)&lt;br/&gt;AS&lt;br/&gt;&lt;br/&gt;SET NOCOUNT ON&lt;br/&gt;&lt;br/&gt;INSERT&lt;br/&gt;INTO&lt;br/&gt;    ELMAH_Error&lt;br/&gt;    (&lt;br/&gt;        ErrorId,&lt;br/&gt;        Application,&lt;br/&gt;        Host,&lt;br/&gt;        Type,&lt;br/&gt;        Source,&lt;br/&gt;        Message,&lt;br/&gt;        [User],&lt;br/&gt;        AllXml,&lt;br/&gt;        StatusCode,&lt;br/&gt;        TimeUtc&lt;br/&gt;    )&lt;br/&gt;VALUES&lt;br/&gt;    (&lt;br/&gt;        @ErrorId,&lt;br/&gt;        @Application,&lt;br/&gt;        @Host,&lt;br/&gt;        @Type,&lt;br/&gt;        @Source,&lt;br/&gt;        @Message,&lt;br/&gt;        @User,&lt;br/&gt;        @AllXml,&lt;br/&gt;        @StatusCode,&lt;br/&gt;        @TimeUtc&lt;br/&gt;    )&lt;br/&gt;&lt;br/&gt;GO&lt;br/&gt;SET QUOTED_IDENTIFIER OFF &lt;br/&gt;GO&lt;br/&gt;SET ANSI_NULLS ON &lt;br/&gt;GO&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;这样，就完成了配置完毕。采用数据记录日志，需要注意三点：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;注意asp.net版本的不同，对于配置的影响； &lt;li&gt;不同关系型数据库的sql语句可能略有不同，应适当修改； &lt;li&gt;嵌入式数据库无需配置第三步骤（c）； &lt;li&gt;不同数据库的连接字符串有所不同； &lt;li&gt;嵌入式数据库（目前支持SQLite和VistaDB），如果配置中指定的数据文件不存在，则ELMAH会自动创建（权限允许时）；但是，关系型数据库，如果不存在，是不会自动创建的。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;其他数据库的配置也是类似的，就不多说了。完全可以看&lt;strong&gt;参考文章2&lt;/strong&gt;，介绍了每种连接方式。&lt;/p&gt;&lt;p&gt;（2）文件存储方式&lt;/p&gt;&lt;p&gt;文件存储实际上ELMAH提供了xml文件的存储方式，&lt;strong&gt;每一个报错日志信息生成一个xml文件&lt;/strong&gt;。配置相当简单：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_321016" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_321016').style.display='none'; document.getElementById('Code_Open_Image_321016').style.display='inline'; document.getElementById('Code_Open_Text_321016').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_321016" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_321016').style.display='none'; getElementById('Code_Closed_Image_321016').style.display='inline'; getElementById('Code_Closed_Text_321016').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_321016" class="cnblogs_code_Collapse"&gt;web.config&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_321016"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;  &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;   &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--与数据库方式相比，实际上只有这一句就行了，其中logPath用于指定记录日志的文件夹位置--&amp;gt;&lt;/span&gt;&lt;br/&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;errorLog&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.XmlFileErrorLog, Elmah"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;logPath&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"~/Log/"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;这里，logPath用于指定存储日志xml文件的位置。如果路径指定位置的文件夹不存在，ELMAH是不会自动创建的，另外，并且应该保证asp.net对此目录具有读写权限。&lt;br&gt;&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_100545"&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;（3）内存存储方式&lt;/p&gt;&lt;p&gt;在上面的&lt;strong&gt;ELMAH初体验&lt;/strong&gt;一节中，实际上已经介绍过了。只要不指定任何存储方式，则默认就是内存存储方式了。当然，也可以显式的指定为内存存储方式。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_549288" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_549288').style.display='none'; document.getElementById('Code_Open_Image_549288').style.display='inline'; document.getElementById('Code_Open_Text_549288').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_549288" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_549288').style.display='none'; getElementById('Code_Closed_Image_549288').style.display='inline'; getElementById('Code_Closed_Text_549288').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_549288" class="cnblogs_code_Collapse"&gt;web.config&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_549288"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;  &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"errorLog"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;requirePermission&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"false"&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.ErrorLogSectionHandler, Elmah"&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;   &lt;br/&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;sectionGroup&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;errorLog&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;=&lt;span style="color: #0000ff"&gt;"Elmah.MemoryErrorLog, Elmah"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br/&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;elmah&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&lt;p&gt;但这种方式应该只用于测试阶段，运行于生产环境中的系统，不应该使用此方式，这主要是基于此种方式的工作原理导致的。&lt;/p&gt;&lt;p&gt;内存存储，顾名思义，将日志记录于操作系统分配给应用程序的内存中。应用程序的内存是与应用程序域相关的，这可以保证每个应用程序只能获取和记录属于自己的日志信息。但是，一旦应用程序重启，之前记录的信息将会消失。最简单的例子，如果你用这种方式调试呢，默认是用ASP.NET Development Server作为web服务器，如果这时停止此服务器，则就满足上述条件了（如下图）。另外，断电，发布后IIS的重启等问题，都会导致记录的信息丢失。因此，这种方式只能用于测试用。（当其他方式不知为何不能使用，可以采用此方式来判别一下问题）&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016293582.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asp.net development server" border="0" alt="asp.net development server" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016292469.png" width="467" height="266"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;简单总结一下各种方式：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;数据库存储方式，配置相对麻烦，但对于大规模日志的记录，效率最好； &lt;li&gt;文件存储方式，配置相对简单，每日志一个文件，当数据量很大后，可能会导致巨量文件带来的效率问题； &lt;li&gt;内存存储，配置最简单，但是鉴于以上原因，不应使用于生产环境。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;3.错误日志记录查看&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;按照如上步骤，日志已经被记录到相应介质中了。那么接下来就是如何查询这些日志信息。&lt;/p&gt;&lt;p&gt;实际上第一节中，就简单介绍了查询界面（第四张图），是在没啥可讲的，点点就知道了。这里还是介绍一下吧。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201104/201104191016295468.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="LogQuery" border="0" alt="LogQuery" src="http://images.cnblogs.com/cnblogs_com/lerit/201104/20110419101630385.png" width="750" height="414"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;四个红框，将这个页面分为四个区域：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;区域1&lt;/strong&gt;，菜单区，包括五个内容：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;RSS FEED 也就是rss种子，也就是订阅这一日志的rss连接。 &lt;li&gt;RSS DIGEST 也是rss种子，但是显示的是最近15天的日志信息。 &lt;li&gt;DOWNLOAD LOG 下载日志，以cvs格式，导出日志为文件。 &lt;li&gt;HELP 帮助，连接到ELMAH的主页。 &lt;li&gt;ABOUT 关于，点击后，显示当前ELMAH的版本，源代码信息，以及一个检查是否为最新版的按钮。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;区域2&lt;/strong&gt;，分页，选择相应数字，即可分页显示日志。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;区域3&lt;/strong&gt;，内容区，分为7列：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Host 导致报错信息的访问主机名。 &lt;li&gt;Code 错误的HTTP状态码。 &lt;li&gt;Type 错误类型。 &lt;li&gt;Error 错误的信息。其中再次点击Details，可以详细查看报错信息，与本文图3类似。 &lt;li&gt;User 导致报错信息的访问主机的用户。 &lt;li&gt;Date 发生错误的日期。 &lt;li&gt;Time 发生错误的时间。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;区域4&lt;/strong&gt;，版本与版权声明信息等。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;注：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;从这一篇的介绍可以看出，ELMAH的功能还是很多的，不过也存在一些问题需要解决：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ELMAH配置的数据库连接字符串无法加密，或者无法指定为加密了的connectionstring； &lt;li&gt;ELMAH不能在指定的关系型数据库中自动创建相应对象； &lt;li&gt;日志内容是增长的，但ELMAH没有提供清空日志的功能； &lt;li&gt;查询功能比较薄弱，不能提供过滤条件。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;参考文章：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;1. &lt;a href="http://www.cnblogs.com/n-pei/archive/2011/02/26/1966025.html"&gt;《ASP.NET MVC 3.0学习系列文章—NuGet and ASP.NET MVC 3.0》&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2.&lt;a href="http://elmah.googlecode.com/svn/tags/REL-1.1/samples/web.config"&gt;《ELMAH的web.config详细配置（v1.1）》&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3. &lt;a href="http://www.connectionstrings.com/"&gt;《数据库连接参数 网址》&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/2020565.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html</id><title type="text">ELMAH（ASP.NET错误日志记录与通知）系列文章-概念篇</title><summary type="text">最近在研究几个ASP.NET的开源项目时，发现都在使用ELMAH来作为记录整个应用程序的错误日志，于是拿来小研究了一下，在这里和各位分享一下，如果有使用过的，请多指教。 对于ELMAH，将用三篇系列文章来介绍： 概念篇 基本应用篇 高级应用篇 概念篇 ELMAH（The Error Logging Modules And Handlers），直译过来就是“错误日志模块和处理”，它提供了一个用于集...</summary><published>2011-03-29T01:09:00Z</published><updated>2011-03-29T01:09:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 最近在研究几个ASP.NET的开源项目时，发现都在使用ELMAH来作为记录整个应用程序的错误日志，于是拿来小研究了一下，在这里和各位分享一下，如果有使用过的，请多指教。  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于ELMAH，将用三篇系列文章来介绍：  &lt;ul&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&lt;/strong&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html"&gt;&lt;strong&gt;&lt;u&gt;概念篇&lt;/u&gt;&lt;/strong&gt;&lt;/a&gt;  &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://www.cnblogs.com/lerit/archive/2011/04/19/2020565.html"&gt;基本应用篇&lt;/a&gt;  &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 高级应用篇&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font size="5"&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp; 概念篇&lt;/strong&gt;&lt;/font&gt;  &lt;p&gt;&lt;a href="http://code.google.com/p/elmah/"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ELMAH&lt;/a&gt;（The Error Logging Modules And Handlers），直译过来就是“错误日志模块和处理”，它提供了一个用于&lt;strong&gt;集中记录和通知错误日志&lt;/strong&gt;的机制。它是专用于ASP.NET的完全&lt;strong&gt;可热插拔&lt;/strong&gt;的错误日志记录工具。其特点就是无需ASP.NET程序重新编译，即可通过配置web.config（或machine.config）来实现整个应用程序甚至是IIS中所有ASP.NET应用程序的错误日志记录工作。&lt;u&gt;它支持日志的多种存储方式（各种数据库、XML、内存存储），除了提供一个界面用于查询日志详细信息外，还可以通过E-MAIL、RSS订阅或Twitter发布方式通知错误信息给相关人员。 &lt;/u&gt; &lt;p&gt;&amp;nbsp; &lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103290908346403.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="error0" border="0" alt="error0" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103290908359958.jpg" width="751" height="471"&gt;&lt;/a&gt;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于一个已经运行于生产环境的ASP.NET程序，如果想用ELMAH为程序增加错误日志记录的功能，只需要将ELMAH的dll文件复制到程序的bin目录下，然后修改此ASP.NET程序的web.config文件，就完成了所有的配置工作。以后，想查看系统发生了哪些错误信息，直接输入在web.config中事先指定的页面地址，即可看到日志信息。具体操作请看下一篇：应用篇。本文将简单介绍其基本概念和运行机制。  &lt;p&gt;&lt;font size="4"&gt;1&lt;/font&gt;.ELMAH是一个可热插拔的解决方案。也就是说可以动态的加入到ASP.NET应用程序，而不需要对项目进行重新编译和部署，因为仅需要在web.config中配置，并将一些程序集复制到bin目录下即可，而不需要更改项目中其他任何内容（如果你项目中的代码有可能导致ELMAH无法记录一些异常信息，则需要修改相关代码，具体请看下面第4条）。  &lt;p&gt;&lt;font size="4"&gt;2&lt;/font&gt;.ELMAH仅仅是后台记录未处理的异常，在异常发生时，它并不会改变用户体验，也就是说根据软件自身设置情况，用户依然可能看到黄颜色的报错界面或者被定位到一个错误提示页面。实际上，从用户体验角度，应该制定一个友好的错误页面, 可以&lt;strong&gt;参考文章2&lt;/strong&gt;。  &lt;p&gt;&lt;font size="4"&gt;3&lt;/font&gt;.ELMAH是通过HTTP modules与HTTP handlers来记录和展示软件中未捕获的异常（如果异常通过catch被捕获了就无法记录了，除非捕获后又throw。也就是在异常链上，最终的异常必须抛给了ASP.NET运行时，才可以被捕获）。另外，如果那个未处理的异常是ASP.NET Web service的，因为这种异常不发给HTTP modules，ELMAH也是无法记录到的.这种异常被ASP.NET运行时截获并返回一个SOAP falut（SOAP消息中传输错误及状态信息），因此，如果想记录这种异常，可以创建一个SOAP扩展来监听SOAP faults。  &lt;p&gt;这里简单介绍一下HTTP modules与HTTP handlers：  &lt;p&gt;当一个请求到达IIS，如果这个请求资源被配置为由ASP.NET ISAPI来处理，那么IIS将把这一请求分发给&lt;code&gt;aspnet_isapi.dll&lt;/code&gt;，而这个dll将请求又交给了ASP.NET引擎来处理。在这一引擎里，有多个HTTP modules（可以在web.config中配置），每个请求都要依次通过这些HTTP modules，这就使得各个HTTP modules可以根据自己需要处理这些请求，当经过所有的HTTP modules后，将由一个（只能是一个）HTTP handler来处理这一请求（可以在web.config中配置由哪个HTTP handler来处理，它是根据被请求资源的路径名称或者后缀名来决定，比如可以指定如果请求error.axd这个资源文件，则由名称为ErrorLog的HTTP handler来处理。）。处理后这个HTTP handler将结果返回给用户，其过程是上述的相反过程，通过各个HTTP modules，最终到达用户端，示意图如下：  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103290908352990.gif"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="IC121205" border="0" alt="IC121205" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103290908356337.gif" width="616" height="305"&gt;&lt;/a&gt;&amp;nbsp;下面来看看ELMAH是如果根据如上的机制，来配置httpModules和handlers的。//ELAMH添加了一个HTTP module，从而让每个请求和回应都通过它。当用户请求一个资源而报错后，返回给这个错误信息时，&lt;br/&gt;//当通过了ErrorLog这个HTTP module，ELMAH获取到回应中的错误信息，并记录下来。&lt;br/&gt;&amp;lt;httpModules&amp;gt;  &lt;br/&gt;   &amp;lt;add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" /&amp;gt; &lt;br/&gt;&amp;lt;/httpModules&amp;gt;   &lt;br/&gt;&lt;br/&gt;//当用户想查看记录下来的错误日志时，他将请求叫做elmah.axd的资源文件，这个请求最终被叫做Elmah的这个handler来&lt;br/&gt;//处理，他查询错误日志的信息后，将结果返回给用户，从而实现了查询功能。&lt;br/&gt;&amp;lt;handlers&amp;gt; &lt;br/&gt;  &amp;lt;add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah"/&amp;gt;&lt;br/&gt;&amp;lt;/handlers&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;&lt;font size="4"&gt;4&lt;/font&gt;.ELMAH捕获异常是基于HttpApplication对象的Error事件。如果软件项目中的一些处理导致了HttpApplication事件无法被触发（比如在发生异常后，还没来得及执行Application_Error，就执行了Server.ClearError()方法，他会阻止Error事件的触发，再比如，如果一个异常被try-catch捕获到，并且没有再次throw，那么异常也是不会最终触发Error事件），那ELMAH也就无能为力了。因此要确保这一点。 &lt;p&gt;&lt;font size="4"&gt;5&lt;/font&gt;.既然可以通过配置某一项目的web.config文件并将DLL文件Copy到项目的bin目录下即可实现对某一项目的错误日志记录，那么同样可以通过配置machine.config以及将dll文件置于GAC中，从而实现对IIS中所有ASP.NET项目实现错误日志记录。 &lt;p&gt;&lt;font size="4"&gt;6&lt;/font&gt;.还有一个常用的用于.NET异常处理的解决方案：Exception Management Application Block (EMAB)。他是&lt;a href="http://msdn.microsoft.com/en-us/library/ff650810.aspx"&gt;Enterprise Library&lt;/a&gt;的一个模块。这里并不主要讨论它与ELMAH谁更好，只有哪个更适合某一应用，以下简单对比： &lt;p&gt;(1)：ELMAH的机制决定他只用于ASP.NET，而EMAB同时适用于.NET的WinForm和ASP.NET，但是更适合用于WinForm，因为默认它将日志记录于操作系统的事件查看器，事件查看器适用于记录精简的日志信息，而这对于ASP.NET，如果是运行于共享主机等空间，还需要分配给ASP.NET读写事件查看器的权限，这显然有时候并不可行。当然，EMAB也可定制其他记录日志的方式，但这一切都需要开发人员去实现。 &lt;p&gt;(2)：EMAB不是热插拔的解决方案，需要在Global.asax的&lt;code&gt;Application_Error&lt;/code&gt;中（或其它位置）编写相应代码。而ELMAH是热插拔的，不需要编写代码，不需要重新编译软件。 &lt;p&gt;&lt;font size="4"&gt;7&lt;/font&gt;.其实.NET下的日志记录工具还是不少的，比如著名的Log4net以及&lt;a href="http://msdn.microsoft.com/en-us/library/ff650810.aspx"&gt;Enterprise Library&lt;/a&gt;中的&lt;a href="http://msdn.microsoft.com/en-us/library/ff647242.aspx"&gt;Logging Application Block&lt;/a&gt;。可以说，这些工具相对ELMAH来说，太重量级了，他们可以记录各种日志信息，比如监视代码中变量的变化情况，周期性的记录到文件中供其他应用进行统计分析工作；跟踪代码运行时轨迹，作为日后审计的依据；担当集成开发环境中的调试器的作用，向文件或控制台打印代码的调试信息。因此，如果仅仅是记录ASP.NET的错误日志，ELMAH应该是首选，而其他功能，ELMAH就无能力为了，也就是说，没有所谓好坏，只有哪个更适合之说吧。&lt;/p&gt;&lt;p&gt;基本概念就这样吧，要想深入理解其运行机制，可以参考下面的&lt;strong&gt;参考文章1&lt;/strong&gt;，讲的很详细。&lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;p&gt;&lt;font size="4"&gt;备注：感谢&lt;/font&gt;&lt;a href="http://www.cnblogs.com/zhoufoxcn/"&gt;&lt;font size="4"&gt;周公&lt;/font&gt;&lt;/a&gt;&lt;font size="4"&gt;帮我指出了一个笔误，不知道你会不会解梦啊：）&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;参考文章：&lt;/strong&gt;&lt;br&gt;1.&lt;a href="http://msdn.microsoft.com/en-us/library/aa479332.aspx"&gt;《Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components》&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2.&lt;a href="http://msdn.microsoft.com/en-us/library/h0hfz6fc.aspx"&gt;《customErrors Element (ASP.NET Settings Schema)》&lt;/a&gt; &lt;p&gt;3.&lt;a href="http://www.west263.com/www/info/34203-1.htm"&gt;《ASP.NET中的HTTP模块和处理程序》&lt;/a&gt; &lt;p&gt;4. &lt;a href="http://code.google.com/p/elmah/"&gt;ELMAH主页&lt;/a&gt; &lt;p&gt;5.&lt;a href="http://elmah.googlecode.com/svn/tags/REL-1.1/samples/web.config"&gt;《ELMAH的web.config详细配置（v1.1）》&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/1998396.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/03/22/1991347.html</id><title type="text">调试IIS程序（w3wp.exe）需要事前设置的一个选项</title><summary type="text">最近使用VS2010，采用“附加到进程”方式调试运行于IIS的网站程序过程中，发现如下提示： 正在进行调试的Web服务器进程已由Internet信息服务（IIS）终止。可以通过在IIS中配置应用程序池Ping设置来避免这一问题。有关更多详细信息，请参见“帮助” 根据提示，不难搜索到解决办法。下面简单分析一下导致这一问题的原因及其解决办法。 这一问题主要是因为IIS6、IIS7提供了Applicat...</summary><published>2011-03-22T05:45:00Z</published><updated>2011-03-22T05:45:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/03/22/1991347.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/03/22/1991347.html"/><content type="html">&lt;p&gt;最近使用VS2010，采用“附加到进程”方式调试运行于IIS的网站程序过程中，发现如下提示：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;正在进行调试的Web服务器进程已由Internet信息服务（IIS）终止。可以通过在IIS中配置应用程序池Ping设置来避免这一问题。有关更多详细信息，请参见“帮助”&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345059034.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="error" border="0" alt="error" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345065097.jpg" width="631" height="166"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;根据提示，不难搜索到解决办法。下面简单分析一下导致这一问题的原因及其解决办法。&lt;/p&gt; &lt;p&gt;&lt;font face="宋体"&gt;这一问题主要是因为IIS6、IIS7提供了&lt;strong&gt;Application pool health detection&lt;/strong&gt;（应用程序池健康检测）功能。其&lt;strong&gt;主要原理&lt;/strong&gt;是通过周期性的ping IIS的应用程序池，从而判断应用程序池是否没有反应，如果没有反应，将采取诸如“快速故障保护”等措施保证线程池重新恢复运行。具体措施可以参考&lt;/font&gt;&lt;a href="http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/04cef3ab-c2bb-4e9b-9183-58edcc5fcf23.mspx?mfr=true"&gt;Configure Application Pool Health&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;很显然，之所以我在调试过程中提示了这一信息，是因为当调试这一进程并触发断点后，应用程序池处于中断状态，无法响应上述健康检测的ping。如果这一过程持续时间过长（默认3分钟），则会出现如上提示。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解决方法&lt;/strong&gt;：&lt;/p&gt; &lt;p&gt;首先找到你需要调试的程序所属的应用程序池（我的是DefaultAppPool），然后选择“设置应用程序池默认设置”。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345063984.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="setting-1" border="0" alt="setting-1" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345063951.jpg" width="734" height="260"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;进入设置项后，找到“进程模型”，可以将“ping 最大响应时间”设置的大一些，或者干脆将“启用ping”设置为false。即可。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345066111.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="setting-2" border="0" alt="setting-2" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103221345079981.jpg" width="729" height="185"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：如果是在生产环境下，上述设置应该仅在必要的调试过程中进行，之后应该恢复其默认设置。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/1991347.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/03/22/1991347.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lerit/archive/2011/03/09/1978004.html</id><title type="text">VS2010 的一个小Bug（已报告给Microsoft Connect并得到确认）</title><summary type="text">这个问题还是去年12月份在使用VS2010时发现的，之后报告给了Microsoft Connect(英语很差，见谅，不过发现Microsoft回复中也有两个英文单词拼写错误，难怪我金山词霸半天没查到：）)，并得到确认会在以后进行修复。一直没时间写，今天拿来分享一下。 备注：微软将在3月10日发布Visual Studio SP1，不知这个小问题能否在这次更新中得到修复。 VS2010提供了一个新功...</summary><published>2011-03-09T02:43:00Z</published><updated>2011-03-09T02:43:00Z</updated><author><name>lerit</name><uri>http://www.cnblogs.com/lerit/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lerit/archive/2011/03/09/1978004.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lerit/archive/2011/03/09/1978004.html"/><content type="html">&lt;p&gt;这个问题还是去年12月份在使用VS2010时发现的，之后报告给了&lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/632443/the-values-which-contain-escape-sequences-are-different-in-datatip-and-pinned-datatip"&gt;Microsoft Connect&lt;/a&gt;(英语很差，见谅，不过发现Microsoft回复中也有两个英文单词拼写错误，难怪我金山词霸半天没查到：）)，并得到确认会在以后进行修复。一直没时间写，今天拿来分享一下。&lt;/p&gt; &lt;p&gt;备注：&lt;a href="http://www.cnbeta.com/articles/136667.htm"&gt;微软将在3月10日发布Visual Studio SP1&lt;/a&gt;，不知这个小问题能否在这次更新中得到修复。&lt;/p&gt; &lt;p&gt;VS2010提供了一个新功能，&lt;strong&gt;DataTip&lt;/strong&gt;。以前调试时，如果要看某一个变量的值，常常会用鼠标停留在一个变量上，则会给出一个提示来显示变量的值，但是鼠标移开后就看不到提示内容了。VS2010的DataTip功能则是为了让数据提示更方便的显示出来，将鼠标停留在某个变量上，然后会弹出提示框，接着如果点击提示框的最右边的箭头，则这个提示就会一直悬停住而不会消失，除非手动点击小叉关闭。对DataTip的介绍，可参考&lt;a href="http://kb.cnblogs.com/page/84180/"&gt;VS2010调试利器&lt;/a&gt;的第二部分关于&lt;strong&gt;调试数据提示（DataTip）&lt;/strong&gt;的介绍，这里不再赘述。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Bug重现：&lt;/strong&gt;如上所说，由于调试时可以用两种方式（鼠标悬停和DataTip）同时查看到变量的值，因此在使用过程中，发现在某些特殊情况下，两者的值显示不一致！我发现的导致这种不一致的情景是：当显示StringBuilder对象内容中有转义字符时，如下图：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/lerit/201103/20110309104312133.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="截图00" border="0" alt="截图00" src="http://images.cnblogs.com/cnblogs_com/lerit/201103/201103091043135018.jpg" width="795" height="133"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;通过多次测试，发现，DataTip当见到有转义字符时，进行了完整的转义，而鼠标悬停则只对部分转义字符进行转义，而部分不进行转义（\t、\b等反斜杠加英文字母的情况没转义），无论是什么规律，对于同一个对象，同一时刻，给出用户不同的调试信息，显然是有问题的。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lerit/aggbug/1978004.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lerit/archive/2011/03/09/1978004.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
