欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

對微軟Web Deploy的一次艱難調試

系統 2108 0

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

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

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

image

  折騰了一個多小時,終于想到之前發布都是成功的,可能因為在上線前一天,改了很多東西。于是我在給我幫忙的實習生電腦上試了下,上面的代碼還是舊的,結果她那邊可以發布成功。我拿到舊代碼,在本機同樣成功。其實本來直接將站點手工復制到服務器上也沒什么大不了,但我這個人比較愛鉆牛角尖,既然排除了的發布工具或服務器端突然秀逗的原因,那就只能是代碼的原因。于是采用折半排除大法,排除一部分文件在項目外,再嘗試發布。直到最后,才找到罪魁禍首-正是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;我看到了一個是程序員,一個是大師,你感覺到了嗎?

對微軟Web Deploy的一次艱難調試


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

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

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美精品第三页 | 91草莓| 欧美久久久久 | 天天摸日日干 | 好吊日在线观看 | 国产福利在线观看永久免费 | 亚洲久草 | 日本无码成人片在线观看波多 | 香蕉国产人午夜视频在线观看 | 国产剧情一区二区三区 | 国产最新网址 | 中日欧洲精品视频在线 | 久热网站 | 99久久产在线 | 日韩色视频 | 国产高清性xxxxxxxx | 亚洲视频在线看 | 伊人9999| 日韩欧美精品综合一区二区三区 | 久久在视频 | 国产精品永久免费视频 | 深夜福利网站 | 久9视频这里只有精品8 | 亚洲欧美精品伊人久久 | 久久国产精品-国产精品 | 欧美第一色| 中文字幕精品视频 | 多女多p多杂交视频在线观看 | 久久乐国产精品 | 四虎网址 | 精品在线一区二区 | 亚洲一级成人 | 浮力影院国产第一页 | 亚洲一区影院 | 国产亚洲综合成人91精品 | 婷婷在线网站 | 伊人欧美 | 91tv最新永久在线地址 | 亚洲精品一区中文字幕乱码 | 亚洲天堂欧美在线 | 91精品国产91久久久久久 |