1. Fiddler 跟 TFS 相衝的問題解決 - II

    <<續上篇>>

    計劃好之後, 我打算的流程是這樣:

    1. Fiddler 存下目前的 Proxy Config
    2. Fiddler 把 WinINET 的 Proxy 改為 127.0.0.1:8888
    3. 在 OnAttach 裡加上自定的 Script, 就抄 (2) 的 CODE 改一改再把我要的值加上去

    作法想好後, 連放 CODE 的地方都弄好了. Fiddler 直接用現成的 .net language 當成 script 使用, 就是直接修改 CustomRules.js 這個檔案. 裡面已經定義好 Method: OnAttach( ), 會在 Fiddler 開始 Capture Traffic 後被呼叫, 看來我要做的事 (3) 只要放在裡面就好, (1) 及 (2) 是 Fiddler 自己本來就會做的部份.

    而 Fiddler 不再 Capture Traffic 時, 我什麼都不必作, 因為 Fiddler 預設的動作就是把 (1) 存下來的東西再填回 IE 的 Proxy 設定...

    其實這方法還可以解決附帶的幾個問題, 像我在家用 VPN, 或是在公司用無線網路, Fiddler 也常常失效, 因為它預設都只改 [區域連線] 的 Proxy 設定... 不過 IE 又很貼心的讓每個 network adapter 都可以有自己的 proxy settings, 你只要不是用預設的 NIC 就沒救了. 雖然手動把你用的網路連線 proxy 改為 127.0.0.1:8888 就可以動, 不過每次都改也是很煩人...

    好, 有足夠的誘因了, 開始動手...

     

    A 計劃:

    開始用 Reflector 追 Fiddler 的程式, Bingo... 主程式是 Fiddler.frmViewer, 我要的東西就藏在 oProxy 這個 static field 裡. 看了 oProxy 的型別是 Fiddler.Proxy, 繼續追下去... My God.... 截到宣告如下:

       1:  internal WinINETProxyInfo piPrior;
    
       2:  private WinINETProxyInfo piThis;
    

    piPrior 放的是存起來的設定, piThis 則是被 Fiddler 填入的設定, 沒救, private field... 連用 reflection 的機會都沒有, 只好放棄, 想第二條路...

     

    B 計劃:

    碰到鐵板, 繼續鑽別條路. 我先自己開個 console application 做簡單的測試, 先把 Fiddler.exe 改成 Fiddler.dll, 然後設定 project 參考這個 .dll, 依照 Fiddler 主程式的用法測了一下這段 code:

       1:  static void Main(string[] args)
    
       2:  {
    
       3:      WinINETProxyInfo proxy = new WinINETProxyInfo();
    
       4:      proxy.GetFromWinINET(null);
    
       5:      proxy.sHostsThatBypass = "*.chicken-house.net;*.hinet.net;";
    
       6:      proxy.SetToWinINET(null);
    
       7:      return;
    
       8:  }
    

    哇哈哈, 真的有效, Run 過之後開控制台的 Internet Options 裡的 PROXY 設定, 真的被改過來了, 心想太好了, 這段 code 貼到 CustomRule.js 就一切搞定...

    對, 這麼順利的話就不會分兩篇了, 誰曉得貼上去後 Fiddler 就給我唉這段 message:

    按了 [確定] 後就變這樣:

    真是嘖嘖嘖... 大概猜的出問題在那, 這種外掛的 script 多半動態 Load Script, 動態 compile, 同時會載入到另一個獨立的 AppDomain, 看起來在 script 可用的範圍內, 是存取不到 Fiddler.WinINETProxyInfo 這個類別...

    AppDomain 真是讓人恨的牙癢癢的, 不過它還真是個很棒的設計. 在 .net 的世界, AppDomain 可以在不降低效能的前題下, 達到如傳統 OS Process 的安全隔離層級, 而且又有 thread 的快速 share data 方式... 可以說有 process 的好處, 又無 process 的負耽.

    講那麼多幹嘛? 問題還是無解... 不過有了上面 console application 試驗後, 至少讓我證明這條路是可行的, 只不過 script 得再繞一條路試看看...

     

    C 計劃:

    我的目的只是要在 CustomRule.js 的 OnAttach 裡執行那四行 code 而以, 只好脫褲子放屁了, 四行可以搞定的事多花幾行來寫, 把之前搭配 Attribute 寫 Library 學到的那套 Reflection 搬出來用, 原本的這四行 code:

       1:  WinINETProxyInfo proxy = new WinINETProxyInfo();
    
       2:  proxy.GetFromWinINET(null);
    
       3:  proxy.sHostsThatBypass = "*.chicken-house.net;*.hinet.net;";
    
       4:  proxy.SetToWinINET(null);
    

    改寫為:

       1:  System.Reflection.Assembly fiddler = System.Reflection.Assembly.LoadFrom(@"Fiddler.exe");
    
       2:   
    
       3:  object proxy = fiddler.CreateInstance("Fiddler.WinINETProxyInfo");
    
       4:  Type proxyType = fiddler.GetType("Fiddler.WinINETProxyInfo");
    
       5:   
    
       6:  proxyType.GetMethod("GetFromWinINET").Invoke(proxy, new object[] { null as string });
    
       7:  proxyType.GetProperty("sHostsThatBypass").SetValue(proxy, "http://ld-fsweb.learningdigital.com:8080;", null);
    
       8:  proxyType.GetMethod("SetToWinINET").Invoke(proxy, new object[] { null as string });
    

     

    很好, 繞了一大圈, 結果還是不行... [:@], 結果跟 B 計劃一樣, 我就不貼了...

     

    D 計劃:

    好, 最後一招了, 別轉台... 實在是受不了了, 最沒品的那招拿出來用... 把 B 計劃寫的 Console App 拿來用, 東西都寫好丟在 Fiddler 的目錄, 檔名叫 myproxycfg.exe, 然後在 CustomRules.js 裡呼叫它...

       1:  System.Diagnostics.Process.Start("myproxycfg.exe");
    

     

     

    嗯, Fiddler 啟用 Capture Traffic 後, 果然就 OK 了, 這招都出了還不行就沒辦法了.. 吊個 .exe 在那邊看了實在很礙眼, 不過眼不見為淨啦, 可以 work 就好...

    IE Proxy 的設定真的如我所料, Fiddler 啟動後就改掉了, Fiddler 停掉後就一切揮復原狀, [Y][Y][Y], 以後不用為了 TFS 要 Get Latest Version 還得去關 Fiddler ....

    果然懶才是資訊科技進步的原動力啊, 哈哈.., 謝謝收看.

    2007/04/24 .NET Tips 技術隨筆

  2. Fiddler 跟 TFS 相衝的問題解決 - I

    Fiddler, 應該不用我多介紹了, 一套很好用的 Http Debugging Tool. 它的原理是把自己當成 Proxy, 讓所有的 Http 流量都經過它再轉出去, 讓你能看到你的程式到底跟網站講了那些話, 尤其是新興的 AJAX 更需要這種 Tools, 因為一堆東西是你按右鍵 + view source 所看不到的...

    不過以上不是重點, 重點是我常常搭配 visual studio 2005 一起使用, 每當 Fiddler 開啟, 我再使用 vs2005 的 TFS 相關功能時, vs2005 跟 TFS 中間的 http connection 就被欄下來不動了.

     

    Fiddler Log:

    HTTP 401, 看起來就像是 vs2005 傳出去的身份驗證機制沒有成功的通過 Fiddler 傳到 server, 導至 server 回應 401 回報沒有權限... vs2005 回的 ERROR MESSAGE 則是看起來跟這件事一點關聯都沒有...

    正規的解法應該是想辦法讓 vs2005 的驗證能過 Fiddler Proxy ... 不過太懶了, 我發現只要開 Fiddler 等它自動改完 IE Proxy Settings 後再把 TFS 網址加到 bypass host list 後就一切正常了, 所以腦筋就動到怎麼讓 Fidder 自動調整的 Proxy Settings 能自動把 TFS 網址加到忽略清單內. 我打算的流程是這樣:

    1. Fiddler 存下目前的 Proxy Config
    2. Fiddler 把 WinINET 的 Proxy 改為 127.0.0.1:8888
    3. 在 OnAttach 裡加上自定的 Script, 就抄 (2) 的 CODE 改一改再把我要的值加上去

    想的很好, 就開始動工了, 不過開始動工才發現, 原來一路上困難重重... -_-

    << 下期待續 >>

    2007/04/23 .NET Tips 技術隨筆

  3. WMCmd.vbs 在 VISTA 下執行會導至 cscript.exe 發生錯誤...

    我用的冷門工具在 vista 裡又碰到問題了 [:S]

    Windows Media Encoder 9 附的 script: WMCmd.vbs, 在 vista 下執行時, cscript.exe 就會因為 DEP 的原因發生問題, 被攔下來, 看來 Microsoft 自己的 code 也是到處都藏著地雷, 隨便也碰到 data segment 被執行的狀況, 然後 DEP 就啟動了...

    本想把 DEP 關掉了事, 不過 cscript.exe 在 vista 還被限定非得開啟 DEP 不可, Orz, 只好 Google / Microsoft support 查看看有沒有解了. 沒想到運氣還真不錯, 果然在官方網站找到 solution:

     

    FIX: You may experience issues when you use Windows Media Encoder 9 Series on a computer that is running Windows Vista

    URL: http://support.microsoft.com/kb/929182/en-us

     

    果然 FIX 裝了就一切正常, 以前寫的批次轉 video script 繼續用, 剩下只缺 Canon 補上 .CRW 的 codec 了 [:D]

    2007/04/16 Tips 技術隨筆 有的沒的

  4. 手癢亂裝 MyATM...

    最近真是, 裝什麼, 什麼就出問題...

    因為信用卡繳費期限要到了, 換了 Vista 用 WebATM 又有點怪怪的, 其它銀行的都可以, 唯讀台新的 ATM 一連就當, 看到它有 MyATM 這個小工具可以安裝, 就裝起來看看...

    沒想到它只是個很惹人厭的 Applet, 躲在右下角, 等你 ATM 卡片塞進去就跳網頁出來... (這樣的話我幹麻裝... [:@])

    沒想到要移除後, 新增移除工具竟然跟我講權限不足? 搞什麼, Administrators 還沒權限是要怎樣才有權限? 不過這年頭 Error Message 都亂寫一通, 一堆不知道啥怪問題的就說是權限... 讓我想起我追過的一堆 Bug, 都是 try { ... } catch 攔到不知明的 Exception, 就一概顯示 "權限不足, 請聯絡系統管理員" ...

    最好系統管理員真的這麼了不起, 咳咳, 沒事開了 Registry Editor 出來找看看, search "台新銀行", 找到這筆:

    電腦\HKEY_LOCAL\MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F25E1429-F70A-4843-8885-84CE5E18C352}\UninstallString

    C:\Program Files\\InstallShield Installation Information\{F25E1429-F70A-4843-8885-84CE5E18C352}\setup.exe "-removeonly"

    發現它的路逕怎麼多了一個反斜線? 拿掉之後再移除一次就 OK 了

    真是它ㄨㄨㄨ的... 還好我無聊, 不然安裝程式清單老卡一個不想要的軟體, 看了還真礙眼... 哈哈, 弄完了, 收工!

     

    2007/04/10 Tips 技術隨筆 有的沒的

  5. 原來 System.Net.Mail 也會有 Bug ...

    果然老外寫的程式, 就是容易忽略掉亞洲語系的需求... 為了這個 Bug, 足足浪費我兩天的時間 [:@], 既然糾出來了, 當然要講一下... 先來看這段 sample code:  

    MailMessage mail = new MailMessage();
    Encoding chtEnc = Encoding.GetEncoding(950);
    mail.From = new MailAddress("peter@chicken-house.net", "吳小皮", chtEnc);
    mail.To.Add(new MailAddress("annie@chicken-house.net", "吳小妹", chtEnc));
    mail.Subject = "今天天氣很好";
    mail.SubjectEncoding = chtEnc;
    mail.Body = "blah blah blah...";
    (new SmtpClient()).Send(mail);
    嗯, 執行的很好, 收的到 MAIL, 編碼也沒問題. 不過沒有回應的 code 總是不大 friendly, 加印一行 message 看看...
        MailMessage mail = new MailMessage();
        Encoding chtEnc = Encoding.GetEncoding(950);
        mail.From = new MailAddress("peter@chicken-house.net", "吳小皮", chtEnc);
        mail.To.Add(new MailAddress("annie@chicken-house.net", "吳小妹", chtEnc));
        mail.Subject = "今天天氣很好";
        mail.SubjectEncoding = chtEnc;
        mail.Body = "blah blah blah...";
        Console.WriteLine("準備寄信 (From: {0})", mail.From);
        (new SmtpClient()).Send(mail);
    My God !!! 啥米, 這樣就錯? 而且錯的地方讓我丈二金剛摸不著頭腦... 執行的環境試過 XP, 2003, Vista, 中英文版, 都有 windows update 更新所有的 patch, 除了訊息有中英文版不同之外, 錯誤通通一樣, Exception Dump 如下:
            準備寄信(From: "吳小皮")未處理的例外狀況: System.Net.Mail.SmtpException: 
            傳送郵件失敗。	--->System.FormatException: 
            標頭值中找到無效的字元。	
            於 System.Net.Mime.HeaderCollection.Set(String name, String value)   
                於 System.Net.Mail.Message.PrepareHeaders(Boolean sendEnvelope)   
                於 System.Net.Mail.Message.Send(BaseWriter writer, Boolean sendEnvelope)   
                於 System.Net.Mail.SmtpClient.Send(MailMessage message)
                -- - 內部例外狀況堆疊追蹤的結尾-- - 
                於 System.Net.Mail.SmtpClient.Send(MailMessage message)   
                於 Program.Main()
    真是它ㄨㄨㄨ的, 怎麼會這樣? 我實際的情況比較慘, 是加了一堆 Console.WriteLine( ) 後才突然發現有問題, 跟本搞不清楚怎麼回事... 試到最後, 確定加了 Console.WriteLine( ) 會有問題, 問題是, 這行到底有什麼了不起的? 不過就是 mail.Form.ToString() ... [:|] 決定繼續挖下去, 先從 Exception 開始查. 前面有一大堆不好追的就跳過去了, 從 dump 的 call stack, 再用 Refactor 去反組譯 .net 的 assembly, 最後這裡看起來最像是 Exception 的源頭:
    class: System.Net.Mime.HeaderCollection method: public override void Set(string name, string value)
    截錄片段 source code:
      if (!MimeBasePart.IsAnsi(value, false))
      {
          throw new FormatException(SR.GetString("InvalidHeaderValue"));
      }
    怎麼看都沒問題, 追過 IsAnsi( ), 裡面沒啥特別的 code, 就 char 的值小於 0xff 就判定 pass. 所以問題應該出在 value 的值送進來判定時就已經有問題了... 再往上追, value 的源頭是 MailAddress 物件的 .ToEncodedString( ) 來的:
    class: System.Net.Mail.MailAddress
        internal string ToEncodedString()
        {
            if (this.fullAddress == null)
            {
                if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
                {
                    StringBuilder builder = new StringBuilder();
                    MailBnfHelper.GetDotAtomOrQuotedString(this.encodedDisplayName, builder);
                    builder.Append(" <");
                    builder.Append(this.Address);
                    builder.Append('>');
                    this.fullAddress = builder.ToString();
                }
                else
                {
                    this.fullAddress = this.Address;
                }
            }
            return this.fullAddress;
        }
    然後跟加了就會出問題的 ToString( ) 比對著看:
      public override string ToString()
      {
          if (this.fullAddress == null)
          {
              if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
              {
                  StringBuilder builder = new StringBuilder();
                  builder.Append('"');
                  builder.Append(this.DisplayName);
                  builder.Append("\" <");
                  builder.Append(this.Address);
                  builder.Append('>');
                  this.fullAddress = builder.ToString();
              }
              else
              {
                  this.fullAddress = this.Address;
              }
          }
          return this.fullAddress;
      }
    
    Ouch, 真是想罵人, 問題就在這裡... 看起來是 Microsoft 工程師為了避開重複作編碼的動作, 每次呼叫 ToEncodedString( ) 及 ToString( ) 時都會去看看 fullAddress 這個 private field 是否有值? 有的話代表之前已經作過編碼了, 就直接撿現成. 問題出在第一次呼叫時, 編碼的動作在 ToString( ) 及 ToEncodedString( ) 各寫了一次 (果然沒有做好 refactoring ... 哈哈), 結果 ToString( ) 的這份 code implementation 是錯的, 跟本沒編碼 [:@] ... 我沒事雞婆在寄信前呼叫 ToString( ) 印 Messabe 算我運氣背, 就碰到這個 Bug.. [:@]
    花了近兩天, 不過最後有找到 Bug, 而且還是 Microsoft 的 Bug, 哈哈, 總算證明人不是我殺的 [:D], submit Microsoft 的 Bug 有獎金嘛? 不只 Bug, 連問題都找到了... 有的話通知一下 [:D]

    2007/04/06 .NET Tips 技術隨筆