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…. 截到宣告如下:

    internal WinINETProxyInfo piPrior;
    private WinINETProxyInfo piThis;
    

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

    B 計劃:

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

    static void Main(string[] args)
    {
        WinINETProxyInfo proxy = new WinINETProxyInfo();
        proxy.GetFromWinINET(null);
        proxy.sHostsThatBypass = "*.chicken-house.net;*.hinet.net;";
        proxy.SetToWinINET(null);
        return;
    }
    

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

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

    Fiddler script error

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

    Fiddler script compilation error

    真是嘖嘖嘖… 大概猜的出問題在那, 這種外掛的 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:

    WinINETProxyInfo proxy = new WinINETProxyInfo();
    proxy.GetFromWinINET(null);
    proxy.sHostsThatBypass = "*.chicken-house.net;*.hinet.net;";
    proxy.SetToWinINET(null);
    

    改寫為:

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

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

    D 計劃:

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

    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 就被欄下來不動了.

    TFS connection blocked

    Fiddler Log:

    Fiddler HTTP 401 error

    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 了

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

    MyATM uninstall registry fix

    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 技術隨筆