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

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

系統(tǒng) 2018 0

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

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

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

image

  折騰了一個(gè)多小時(shí),終于想到之前發(fā)布都是成功的,可能因?yàn)樵谏暇€前一天,改了很多東西。于是我在給我?guī)兔Φ膶?shí)習(xí)生電腦上試了下,上面的代碼還是舊的,結(jié)果她那邊可以發(fā)布成功。我拿到舊代碼,在本機(jī)同樣成功。其實(shí)本來(lái)直接將站點(diǎn)手工復(fù)制到服務(wù)器上也沒什么大不了,但我這個(gè)人比較愛鉆牛角尖,既然排除了的發(fā)布工具或服務(wù)器端突然秀逗的原因,那就只能是代碼的原因。于是采用折半排除大法,排除一部分文件在項(xiàng)目外,再嘗試發(fā)布。直到最后,才找到罪魁禍?zhǔn)祝莣eb.config文件。有點(diǎn)出乎意料,原以為這個(gè)文件不用編譯,直接復(fù)制就可以。

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

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

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

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

    logger.LogError(uri.LocalPath, exception.LineNumber, exception.LinePosition, exception.Message, 
    
      new object
    
    [0]);

    flag = 
    
      false
    
    ;

}


  

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

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

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

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

  找到Execute方法,我選了幾個(gè)可能的關(guān)鍵點(diǎn),分別插入了一段IL代碼,來(lái)將下一個(gè)函數(shù)調(diào)用的參數(shù)值保存到日志文件中。代碼可以先用C#寫好,在Reflector中查看編譯的程序集,把IL復(fù)制過去,再根據(jù)上下文修改下如何獲取要保存的參數(shù)即可。比如這行代碼:xmlTarget = OpenSourceFile( this .Source, this .sourceIsFile),對(duì)應(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ù)膬蓚€(gè)字符串合并成一個(gè)(原來(lái)?xiàng)S腥齻€(gè)變量,現(xiàn)為兩個(gè))
??????? 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命令進(jìn)行編譯,又修正一些錯(cuò)誤,基本都復(fù)制多或少一塊造成的。編譯成功后,將原位置的Microsoft.Web.Publishing.Tasks.dll文件備份后替換掉,在Visual Studio中發(fā)布,卻又報(bào)錯(cuò)了,說(shuō)“簽名不匹配”,無(wú)法加載dll。

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

  運(yùn)氣非常好,因?yàn)槿罩疚募兄欢嗔藘尚校f(shuō)明還就是OpenSourceFile方法出錯(cuò)了。Source屬性正是網(wǎng)站項(xiàng)目web.config文件的絕對(duì)路徑,sourceIsFile值為True。在Reflector中進(jìn)入OpenSourceFile方法,更簡(jiǎn)單,只有聊聊幾行:

    
      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方法,目標(biāo)精確已夠,可以收網(wǎng)抓捕嘍。接著可以建一個(gè)測(cè)試工程,就把這個(gè)類,以及與其相關(guān)的類代碼,從Reflector中拷貝出來(lái)。看上去代碼量也不少,不過有些比如說(shuō)是用來(lái)寫Xml,可以直接去掉。編譯通過后,F(xiàn)5調(diào)試運(yùn)行。終于,剝開重重迷霧后,這次看到異常的廬山真面目:XmlAttributePreservationDict類ReadPreservationInfo(string elementStartTag)方法拋出的XmlException-“ 有未閉合的字符串。 第 3 行,位置 47 。”

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

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

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

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

    
      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
    
    ;

    }

}


  

  這段代碼不長(zhǎng),從個(gè)人角度說(shuō),我傾向于用一個(gè)全局的而不是局部的StringBuilder變量。導(dǎo)致本文問題出現(xiàn)在while語(yǔ)句的判斷條件上,0x3e正是右尖括號(hào)的ASC碼,以尖括號(hào)的出現(xiàn)作為節(jié)點(diǎn)結(jié)束標(biāo)志,顯然沒有做到完全的嚴(yán)謹(jǐn)。不知道是微軟的開發(fā)人員偷工減料,還是對(duì)XML的理解有點(diǎn)小偏差。其實(shí),個(gè)人覺得發(fā)布網(wǎng)站有必重寫這么多東西嗎?

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

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


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

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

【本文對(duì)您有幫助就好】

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

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日本一区二区三区免费在线观看 | 国国产自国偷自产第38页 | a级在线观看视频 | 亚洲国产一区在线精选 | 免费在线毛片 | 久久99精品综合国产首页 | 久久精品在线免费观看 | 国产福利在线免费观看 | 久久精品国产99国产精品免费看 | 图片亚洲va欧美va国产综合 | 99视频精品全部免费免费观 | 亚洲激情区| 国产精品久久久久久久小唯西川 | 亚洲综合视频在线 | 国内精品伊人久久久久7777人 | 精品久久中文久久久 | 色六月丁香 | 精品一久久香蕉国产线看观看下 | 欧美一级片 在线播放 | 在线500福利视频国产 | 国产在线观看一区二区三区四区 | 亚洲午夜日韩高清一区 | 亚洲一级片免费 | 99视频在线观看视频一区 | 日本操操操 | 亚洲视频综合网 | 亚洲成片观看四虎永久 | 高清欧美一级在线观看 | 久久久久久久综合狠狠综合 | 青草视频在线观看免费资源 | 国产色啪午夜免费视频 | 欧美伊人 | 久久刺激| 深夜男人天堂 | 精品一本久久中文字幕 | 亚洲网站在线看 | 国产精品美女视频 | 日本不卡一区二区三区 | 在线成人爽a毛片免费软件 在线成人天天鲁夜啪视频 在线成人亚洲 | 国产性较精品视频免费 | 亚洲日本va中文字幕婷婷 |