2011年初开始做一个项目,开始体验使用微软网站发布工具来发布网站。在服务器端安装发布服务后,可以在Visual Studio界面中右键点击Web项目,再点发布,第一次填好发布设置,以后就可以实现一键发布,虽然还有不少高级功能没有用到,不过已经方便得不敢相信了。敏捷开发的一个要素不就是每日构建吗,开发过程中,每天下班前Check In代码(Visual Studio装了Anksvn插件),再发布到服务器上,连一分钟都不用。

  具体步骤这里不介绍了,大家有兴趣可以看下Scott Guhire的博客。顺便说一下,那个WebPlatform Installer要比我当时逐个网上搜索下载方便多了,却要你先安装.Net 2.0,明显无理要求嘛,我只装了.Net 4.0。只要把安装包文件提取出来,再改下其config文件让其兼容4.0就可以了。

  按计划过年前,要发布Beta版本,几名领导会来观看演示。可就在演示前,出现了麻烦,站点怎么也部署不上去了。出现下面的错误:

  折腾了一个多小时,终于想到之前发布都是成功的,可能因为在上线前一天,改了很多东西。于是我在给我帮忙的实习生电脑上试了下,上面的代码还是旧的,结果她那边可以发布成功。我拿到旧代码,在本机同样成功。其实本来直接将站点手工复制到服务器上也没什么大不了,但我这个人比较爱钻牛角尖,既然排除了的发布工具或服务器端突然秀逗的原因,那就只能是代码的原因。于是采用折半排除大法,排除一部分文件在项目外,再尝试发布。直到最后,才找到罪魁祸首-正是web.config文件。有点出乎意料,原以为这个文件不用编译,直接复制就可以。

  原因也找到了,是前一天将web.config的<appsettings>加了许多项,很多项中含有转义字符如“><&”之类,删掉这些项就可以了,然后把web.config手工拷到服务器网站根目录下。

  演示进行得比较顺利,领导们接着又提出了几项新功能。过年回来后,尽管忙得不可开交,而我还一直纠结着这个令人摸不着头脑的错误。

  随着站点部署上线,开发告一段落,我终于腾出手来,想把这个问题搞个水落石出。

  首先要找到Microsoft.Web.Publishing.Tasks这个程序集,和一般.Net Framework程序集的不同,它是在C:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\Web目录下。根据错误信息,用Reflector翻出了ParameterizeTransformXml.Execute方法。

bool flag = true;
IXmlTransformationLogger logger = new TaskTransformationLogger(base.Log, this.StackTrace);
XmlTransformation transformation = null;
XmlTransformableDocument xmlTarget = null;
try
{logger.StartSection(SR.GetString("BUILDTASK_TransformXml_TransformationStart", new object[] { this.Source }), new object[0]);xmlTarget = OpenSourceFile(this.Source, this.sourceIsFile);logger.LogMessage(SR.GetString("BUILDTASK_TransformXml_TransformationApply", new object[] { this.Transform }), new object[0]);transformation = OpenTransformFile(this.Transform, this.transformIsFile, logger);this.storageDictionary.TokenFormat = this.TokenFormat;this.storageDictionary.UseXpathToFormParameter = this.UseXpathToFormParameter;transformation.AddTransformationService(this.storageDictionary.GetType(), this.storageDictionary);flag = transformation.Apply(xmlTarget);if (flag){logger.LogMessage(SR.GetString("BUILDTASK_TransformXml_TransformOutput", new object[] { this.Destination }), new object[0]);this.resultXml = SaveTransformedFile(xmlTarget, this.Destination, this.destinationIsFile);}
}
catch (XmlException exception)
{Uri uri = new Uri(exception.SourceUri);  //错误抛出源logger.LogError(uri.LocalPath, exception.LineNumber, exception.LinePosition, exception.Message, new object[0]);flag = false;
}

  一目了然,这个方法处理异常的代码出现了的逻辑问题。项目已经上线,我目的已不是让远程发布顺利完成,而是找到真正的错误原因。XmlException是从哪里抛出的呢?对于我这种只用IDE调试的菜鸟来说,有点麻烦。

  马上想到的,是用一个程序加载此程序集,通过抛出异常的InnerException属性的堆栈,应该能找到异常源。问题是怎么启动这个程序集,我乡下人,怕黑不喜欢研究命令行。这里我找到一个不错的借口,即使找到异常源,也没法去调试(这不是.Net Framework一部分,没有源代码下载的)。

  要想调试代码,还得求助于Reflector。不过这个程序集估计有几万行代码,不能指望代码导出来,想把它改得编译通过有点悬。所以争取只导出最少的,与错误相关的代码。既要定位到异常源,还要获悉源方法的上下文变量。束手无策的看了两天源代码,终于决定从IL入手。在园子里,看过多位朋友写过如何改造VSPaste插件,既然同是.Net程序集,我应该也可以在ParameterizeTransformXml.Execute方法中塞一点东西,曝光其一些运行时的真相。

  临渊羡鱼,不如退而结网。耐下性子,学了几天IL语法基础,练了几个示例,然后准备开刀了。由于reflector导出的IL也有问题,所以手术刀还是用ildasm工具,直接转储就可以了。会生成三个文件,扩展名为res和resources的两个不用管,只打开扩展名il的那个文件,有4兆多,所以还是用一个给力点的文本编辑器吧,我用的是NotePad++。

  找到Execute方法,我选了几个可能的关键点,分别插入了一段IL代码,来将下一个函数调用的参数值保存到日志文件中。代码可以先用C#写好,在Reflector中查看编译的程序集,把IL复制过去,再根据上下文修改下如何获取要保存的参数即可。比如这行代码:xmlTarget = OpenSourceFile(this.Source, this.sourceIsFile),对应的IL是:

        IL_0042:  ldarg.0
        IL_0043:  call       instance string Microsoft.Web.Publishing.Tasks.ParameterizeTransformXml::get_Source()
        IL_0048:  ldarg.0
        IL_0049:  ldfld      bool Microsoft.Web.Publishing.Tasks.ParameterizeTransformXml::sourceIsFile
        IL_004e:  call       class Microsoft.Web.Publishing.Tasks.XmlTransformableDocument Microsoft.Web.Publishing.Tasks.ParameterizeTransformXml::OpenSourceFile(string,bool)

在其前面插入:

        ldstr "D:\\pub.log"     //将字符串加载到栈上
        ldarg.0                    //加载自己(this)的引用到栈上                 

   call       instance string Microsoft.Web.Publishing.Tasks.ParameterizeTransformXml::get_Source() //读取属性到栈上
        ldstr "[Source]\r\n"
        call string [mscorlib]System.String::Concat(string, string)  
//将栈顶的两个字符串合并成一个(原来栈有三个变量,现为两个)
        call void [mscorlib]System.IO.File::AppendAllText(string, string) //记录日志,现在栈被清空
        ldstr "D:\\pub.log"
        ldarg.0
        ldfld      bool Microsoft.Web.Publishing.Tasks.ParameterizeTransformXml::sourceIsFile
//读取字段
        box bool  //装箱
        ldstr "[sourceIsFile]\r\n"
        call string [mscorlib]System.String::Concat(object, object)
        call void [mscorlib]System.IO.File::AppendAllText(string, string)

  小心冀冀花了半天,修改完保存,用ilasm命令进行编译,又修正一些错误,基本都复制多或少一块造成的。编译成功后,将原位置的Microsoft.Web.Publishing.Tasks.dll文件备份后替换掉,在Visual Studio中发布,却又报错了,说“签名不匹配”,无法加载dll。

  赶紧又一顿搜索,将程序集IL中.hash语句删除,再编译,替换,重启VS,发布,果然成功了!还是显示原来的错误,不过刚才嵌入的IL代码,如同打入敌人堡垒内部的同志,通过log文件中,成功地送出了致命的情报。

  运气非常好,因为日志文件中只多了两行,说明还就是OpenSourceFile方法出错了。Source属性正是网站项目web.config文件的绝对路径,sourceIsFile值为True。在Reflector中进入OpenSourceFile方法,更简单,只有聊聊几行:

private static XmlTransformableDocument OpenSourceFile(string sourceFile, bool isSourceFile)
{XmlTransformableDocument document2;try{XmlTransformableDocument document = new XmlTransformableDocument{PreserveWhitespace = true};if (isSourceFile){document.Load(sourceFile);}else{document.LoadXml(sourceFile);}document2 = document;}catch (XmlException exception){throw exception;}catch (Exception exception2){throw new Exception(SR.GetString("BUILDTASK_TransformXml_SourceLoadFailed", new object[] { exception2.Message }), exception2);}return document2;
}

  追踪到了XmlTransformableDocument的Load方法,目标精确已够,可以收网抓捕喽。接着可以建一个测试工程,就把这个类,以及与其相关的类代码,从Reflector中拷贝出来。看上去代码量也不少,不过有些比如说是用来写Xml,可以直接去掉。编译通过后,F5调试运行。终于,剥开重重迷雾后,这次看到异常的庐山真面目:XmlAttributePreservationDict类ReadPreservationInfo(string elementStartTag)方法抛出的XmlException-“有未闭合的字符串。 第 3 行,位置 47。”

  异常源找到了,接着要找原因。由于在调试状态,直接可以看到方法参数传进的值出了问题:虽然还不明白这个方法的目的,但elementStartTag不应该一个被截断的Xml节点字符串,而这个节点,正是那天导致发布失败的修改中加入的<appsettings>下的一个<add>节点。

  仔细一看web.config文件中那个出错的节点,顿时让我气得不打一处来。原来对节点中属性中的Html标签中的一个尖括号,没有作转义处理。如今实习生实在是太靠不住了,差点被害死,我还特别强调过要小心转义符号啊。

  当然,微软的代码肯定也有毛病,虽然转义特殊字符是标准做法,但尖括号是居于属性引号中,没理由不能正确解析。实际上,XmlTransformableDocument类的基类XmlDocument(大家都很熟悉了吧),就不存在这种问题。

  ReadPreservationInfo不是最终元凶,真正的元凶是调用它的幕后黑手,我揪它出来,给大家示众:

internal class XmlAttributePreservationProvider
{... ....public XmlAttributePreservationDict GetDictAtPosition(int lineNumber, int linePosition){if (this.reader.ReadToPosition(lineNumber, linePosition)){int num;StringBuilder builder = new StringBuilder();do{num = this.reader.Read();builder.Append((char)num);}while ((num > 0) && (((ushort)num) != 0x3e));if (num > 0){XmlAttributePreservationDict dict = new XmlAttributePreservationDict();dict.ReadPreservationInfo(builder.ToString());return dict;}}return null;}
}

  这段代码不长,从个人角度说,我倾向于用一个全局的而不是局部的StringBuilder变量。导致本文问题出现在while语句的判断条件上,0x3e正是右尖括号的ASC码,以尖括号的出现作为节点结束标志,显然没有做到完全的严谨。不知道是微软的开发人员偷工减料,还是对XML的理解有点小偏差。其实,个人觉得发布网站有必重写这么多东西吗?

  其实,在.Net Framework中对XML操作的核心类-XmlReader,几乎是滴水不漏。看上去命名空间一个是Microsoft,一个是System;我看到了一个是程序员,一个是大师,你感觉到了吗?

转载于:https://www.cnblogs.com/XmNotes/archive/2011/05/31/2063889.html

对微软Web Deploy的一次艰难调试相关推荐

  1. 微软Web Deploy在Windows Server 2003上的安装配置

     1.首先去微软的下载中心下载支持Windows Server 2003的Web deploy最新版本,目前是 3.5 地址如下:http://www.microsoft.com/zh-cn/do ...

  2. 使用Azure SDK 1.4.1中的Web Deploy

    公告    :本博客为微软云计算中文博客  的镜像博客.   部分文章因为博客兼容性问题  ,会影响阅读体验  .如遇此情况,请访问  原博客. 刚刚更新的Azure SDK v1.4.1通过使用We ...

  3. Visual Studio 使用 Web Deploy 发布远程站点

    Ø  简介 本文介绍 Visual Studio 如何使用 Web Deploy发布远程站点,有时候我们开发完某个功能时,需要快速将更改发布至服务器.通常 Visual Studio 可以采用两种方式 ...

  4. Web Deploy发布网站及常见问题解决方法(图文)

    Web Deploy发布网站及常见问题解决方法(图文) Windows2008R2+IIs7.5 +Web Deploy 3.5 Web Deploy 3.5下载安装 http://www.iis.n ...

  5. 微软web服务器组件,iis8.0安装包微软Web服务器组件 官方版

    iis8.0安装包是一款微软推出的Web服务器组件,iis8.0在网络应用服务器的管理.可用性.可靠性.安全性.性能与可扩展性方面提供了许多新的功能今天跟大家分享iis8.0完整安装包下载,本站提供i ...

  6. Web Deploy 发布网站错误 检查授权和委派设置

    Web Deploy发布ASP.NET网站给我们提供方便,配置好后可以很方便地发布网站到IIS服务器. 自安装Web Deploy一年以来,一直都用得好好地. 直到最近,Gitlab-CI自动发布出了 ...

  7. iis8.0php套件包,iis8.0下载|iis8.0安装包微软Web服务器组件官方版 8..8 - 系统天堂

    iis8.0安装包是一款微软推出的Web服务器组件,iis8.0在网络应用服务器的管理.可用性.可靠性.安全性.性能与可扩展性方面提供了许多新的功能今天跟大家分享iis8.0完整安装包下载,本站提供i ...

  8. 微软 web框架_Microsoft .NET Web编程:网站与Web应用程序

    微软 web框架 In .NET 2.0, Microsoft introduced the Web Site.  This was the default way to create a web P ...

  9. VS2015项目部署到服务器,VS 2015使用Web Deploy发布Web 应用

    我的开发环境,win7 64位,使用VS2013: 服务器环境WinServer08 64位. 关于Web Deploy是什么,有什么优势,请参考其他文章,比如:Web Deploy自动部署 本文以s ...

最新文章

  1. python sorted原理_Python sort()和sorted()
  2. Android热修复之 - 收集崩溃信息上传服务器
  3. pdf去除水印方法!【亲测可用】
  4. 初中计算机word教案ppt,初中信息技术课件 用Word处理文字.ppt
  5. C语言预处理命令总结
  6. 【渝粤教育】国家开放大学2018年春季 0341-21T高级英语听力(2) 参考试题
  7. 创建维护计划时,提示“代理XP”组件已作为此服务器安全配置的一部分被关闭...
  8. 反射真的存在性能问题吗?
  9. linq判断集合中相同元素个数_使用Linq获取集合的最后N个元素?
  10. matlab实现QPSK调制解调
  11. 一个iOS模块化开发解决方案
  12. PDF拆分页面的方法,如何拆分PDF页面
  13. 第十二届蓝桥杯省赛JAVA B组杨辉三角形个人题解
  14. 第一部分day5 文件操作
  15. GB28181平台对接接口详解
  16. string find()函数、string::npos的含义、erase()函数
  17. Mysql视图和触发器
  18. 23种设计模式详解(代码讲解、持续更新)
  19. java 辗转相除_Java实现辗转相除法并求取最大公约数、最小公倍数
  20. 一个等号= 二个等号== 三个等号=== 的区别

热门文章

  1. Redis初学:11(Redis的配置文件)
  2. 1 23 456c语言,2014年计算机二级考试C语言模拟题(1)
  3. 【java】如何判断数组中的内容是否重复
  4. 使用SpringBoot发送邮件 在本地测试是好的 放到服务器连接超时问题
  5. 关于MySQL的四种事务隔离级别!
  6. android 开启一个定时线程_ANDROID开发中定时器的3种方法
  7. 卡片右上角三角形效果,按钮点击变色
  8. 数据结构,堆和栈和队列的概念
  9. iOS端Socket连接、发送数据(一)
  10. 即时通讯下数据粘包、断包处理实例(基于CocoaAsyncSocket)