亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

對微軟Web Deploy的一次艱難調(diào)試

系統(tǒng) 2319 0

  2011年初開始做一個項目,開始體驗使用微軟網(wǎng)站發(fā)布工具來發(fā)布網(wǎng)站。在服務(wù)器端安裝發(fā)布服務(wù)后,可以在Visual Studio界面中右鍵點擊Web項目,再點發(fā)布,第一次填好發(fā)布設(shè)置,以后就可以實現(xiàn)一鍵發(fā)布,雖然還有不少高級功能沒有用到,不過已經(jīng)方便得不敢相信了。敏捷開發(fā)的一個要素不就是每日構(gòu)建嗎,開發(fā)過程中,每天下班前Check In代碼(Visual Studio裝了 Anksvn插件 ),再發(fā)布到服務(wù)器上,連一分鐘都不用。

  具體步驟這里不介紹了,大家有興趣可以看下 Scott Guhire的博客 。順便說一下,那個WebPlatform Installer要比我當時逐個網(wǎng)上搜索下載方便多了,卻要你先安裝.Net 2.0,明顯無理要求嘛,我只裝了.Net 4.0。只要把安裝包文件提取出來,再改下其config文件讓其兼容4.0就可以了。

  按計劃過年前,要發(fā)布Beta版本,幾名領(lǐng)導會來觀看演示。可就在演示前,出現(xiàn)了麻煩,站點怎么也部署不上去了。出現(xiàn)下面的錯誤:

image

  折騰了一個多小時,終于想到之前發(fā)布都是成功的,可能因為在上線前一天,改了很多東西。于是我在給我?guī)兔Φ膶嵙暽娔X上試了下,上面的代碼還是舊的,結(jié)果她那邊可以發(fā)布成功。我拿到舊代碼,在本機同樣成功。其實本來直接將站點手工復制到服務(wù)器上也沒什么大不了,但我這個人比較愛鉆牛角尖,既然排除了的發(fā)布工具或服務(wù)器端突然秀逗的原因,那就只能是代碼的原因。于是采用折半排除大法,排除一部分文件在項目外,再嘗試發(fā)布。直到最后,才找到罪魁禍首-正是web.config文件。有點出乎意料,原以為這個文件不用編譯,直接復制就可以。

  原因也找到了,是前一天將web.config的<appsettings>加了許多項,很多項中含有轉(zhuǎn)義字符如“><&”之類,刪掉這些項就可以了,然后把web.config手工拷到服務(wù)器網(wǎng)站根目錄下。

  演示進行得比較順利,領(lǐng)導們接著又提出了幾項新功能。過年回來后,盡管忙得不可開交,而我還一直糾結(jié)著這個令人摸不著頭腦的錯誤。

  隨著站點部署上線,開發(fā)告一段落,我終于騰出手來,想把這個問題搞個水落石出。

  首先要找到Microsoft.Web.Publishing.Tasks這個程序集,和一般.Net Framework程序集的不同,它是在C:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\Web目錄下。根據(jù)錯誤信息,用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
    
    ;

}


  

  一目了然,這個方法處理異常的代碼出現(xiàn)了的邏輯問題。項目已經(jīng)上線,我目的已不是讓遠程發(fā)布順利完成,而是找到真正的錯誤原因。XmlException是從哪里拋出的呢?對于我這種只用IDE調(diào)試的菜鳥來說,有點麻煩。

  馬上想到的,是用一個程序加載此程序集,通過拋出異常的InnerException屬性的堆棧,應(yīng)該能找到異常源。問題是怎么啟動這個程序集,我鄉(xiāng)下人,怕黑不喜歡研究命令行。這里我找到一個不錯的借口,即使找到異常源,也沒法去調(diào)試(這不是.Net Framework一部分,沒有源代碼下載的)。

  要想調(diào)試代碼,還得求助于Reflector。不過這個程序集估計有幾萬行代碼,不能指望代碼導出來,想把它改得編譯通過有點懸。所以爭取只導出最少的,與錯誤相關(guān)的代碼。既要定位到異常源,還要獲悉源方法的上下文變量。束手無策的看了兩天源代碼,終于決定從IL入手。在園子里,看過多位朋友寫過如何改造VSPaste插件,既然同是.Net程序集,我應(yīng)該也可以在ParameterizeTransformXml.Execute方法中塞一點東西,曝光其一些運行時的真相。

  臨淵羨魚,不如退而結(jié)網(wǎng)。耐下性子,學了幾天IL語法基礎(chǔ),練了幾個示例,然后準備開刀了。由于reflector導出的IL也有問題,所以手術(shù)刀還是用ildasm工具,直接轉(zhuǎn)儲就可以了。會生成三個文件,擴展名為res和resources的兩個不用管,只打開擴展名il的那個文件,有4兆多,所以還是用一個給力點的文本編輯器吧,我用的是NotePad++。

  找到Execute方法,我選了幾個可能的關(guān)鍵點,分別插入了一段IL代碼,來將下一個函數(shù)調(diào)用的參數(shù)值保存到日志文件中。代碼可以先用C#寫好,在Reflector中查看編譯的程序集,把IL復制過去,再根據(jù)上下文修改下如何獲取要保存的參數(shù)即可。比如這行代碼:xmlTarget = OpenSourceFile( this .Source, this .sourceIsFile),對應(yīng)的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)??
// 將棧頂?shù)膬蓚€字符串合并成一個(原來棧有三個變量,現(xiàn)為兩個)
??????? call void [mscorlib]System.IO.File::AppendAllText(string, string) //記錄日志,現(xiàn)在 棧被清空
??????? 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中發(fā)布,卻又報錯了,說“簽名不匹配”,無法加載dll。

  趕緊又一頓搜索,將程序集IL中.hash語句刪除,再編譯,替換,重啟VS,發(fā)布,果然成功了!還是顯示原來的錯誤,不過剛才嵌入的IL代碼,如同打入敵人堡壘內(nèi)部的同志,通過log文件中,成功地送出了致命的情報。

  運氣非常好,因為日志文件中只多了兩行,說明還就是OpenSourceFile方法出錯了。Source屬性正是網(wǎng)站項目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方法,目標精確已夠,可以收網(wǎng)抓捕嘍。接著可以建一個測試工程,就把這個類,以及與其相關(guān)的類代碼,從Reflector中拷貝出來。看上去代碼量也不少,不過有些比如說是用來寫Xml,可以直接去掉。編譯通過后,F(xiàn)5調(diào)試運行。終于,剝開重重迷霧后,這次看到異常的廬山真面目:XmlAttributePreservationDict類ReadPreservationInfo(string elementStartTag)方法拋出的XmlException-“ 有未閉合的字符串。 第 3 行,位置 47 。”

  異常源找到了,接著要找原因。由于在調(diào)試狀態(tài),直接可以看到方法參數(shù)傳進的值出了問題:雖然還不明白這個方法的目的,但elementStartTag不應(yīng)該一個被截斷的Xml節(jié)點字符串,而這個節(jié)點,正是那天導致發(fā)布失敗的修改中加入的<appsettings>下的一個<add>節(jié)點。

  仔細一看web.config文件中那個出錯的節(jié)點,頓時讓我氣得不打一處來。原來對節(jié)點中屬性中的Html標簽中的一個尖括號,沒有作轉(zhuǎn)義處理。如今實習生實在是太靠不住了,差點被害死,我還特別強調(diào)過要小心轉(zhuǎn)義符號啊。

  當然,微軟的代碼肯定也有毛病,雖然轉(zhuǎn)義特殊字符是標準做法,但尖括號是居于屬性引號中,沒理由不能正確解析。實際上,XmlTransformableDocument類的基類XmlDocument(大家都很熟悉了吧),就不存在這種問題。

  ReadPreservationInfo不是最終元兇,真正的元兇是調(diào)用它的幕后黑手,我揪它出來,給大家示眾:

    
      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變量。導致本文問題出現(xiàn)在while語句的判斷條件上,0x3e正是右尖括號的ASC碼,以尖括號的出現(xiàn)作為節(jié)點結(jié)束標志,顯然沒有做到完全的嚴謹。不知道是微軟的開發(fā)人員偷工減料,還是對XML的理解有點小偏差。其實,個人覺得發(fā)布網(wǎng)站有必重寫這么多東西嗎?

  其實,在.Net Framework中對XML操作的核心類-XmlReader,幾乎是滴水不漏。看上去命名空間一個是Microsoft,一個是System;我看到了一個是程序員,一個是大師,你感覺到了嗎?

對微軟Web Deploy的一次艱難調(diào)試


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 婷婷综合网站 | 四虎影视永久地址www成人 | 国产福利一区二区在线观看 | 欧美日韩亚洲成人 | 欧美专区在线 | h片在线免费 | 在线成人毛片 | 午夜色站| 久久天天躁狠狠躁夜夜爽蜜月 | 久久国产精品二国产精品 | 久久九九99 | 久久综合日韩亚洲精品色 | heyzo在线播放4k岛国 | 99久久精品免费观看区一 | 亚洲国产一区二区三区综合片 | 99热久久国产精品免费看 | 欧美一级毛片免费观看视频 | 国产成人久久久精品一区二区三区 | 国产亚洲福利精品一区二区 | 91aaa免费观看在线观看资源 | 中文字幕免费在线视频 | 亚洲图片综合区 | 亚洲精品国产一区二区三 | 日韩毛片欧美一级国产毛片 | 99在线观看视频免费 | 婷婷综合在线 | 毛片成人永久免费视频 | 91伊人影院| 麻豆成人在线 | 日韩视频一区二区 | 一区二区三区四区国产精品 | 国产精品婷婷久久爽一下 | 国产色婷婷精品综合在线手机播放 | 在线视频一二三区2021不卡 | 欧美金妇欧美乱妇xxxx | 久久99热在线观看7 久久99热这里只有精品 | 久操香蕉 | 久色网| 亚洲免费在线视频播放 | 奇米影视在线观看 | 亚洲欧美高清在线 |