1. 原來 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 技術隨筆

  2. Canon RAW Codec for Vista 出來了..

    資料來源: http://blogs.msdn.com/pix/archive/2007/03/30/canon-raw-codec-for-vista-release.aspx

    前天才抱怨了一下, WPF 的 WIC 沒有內建 canon raw codec, 沒想到 2007/3/30 已經出來了, 真是失敬 [:p], 不過為什麼只支援 .CR2 啊啊啊啊啊.... 難道用老相機 G2 就註定是這種下場嘛? Orz

    往好方面想, .CRW 的 codec 應該也會出吧? 嗯, 再等一等好了...

    2007/04/04 .NET WPF 技術隨筆 有的沒的

  3. Vista Day 4...

     

    升級 Vista 第四天, 來講幾個令我超不爽的地方....

    1. 內建的工具列, 現在竟然不能拉出工作列了... [:@]
    2. 內建陽春的注音輸入法大改一通... 這一天終於來了 [:'(]
    3. Console 不再能直接拉檔案過去, 代表直接輸入完整路逕了, ouch...
    4. 兩個影像相關的 PowerToys 不能用... ( Image Resizer, RAW Image Viewer)
    5. UAC 真囉唆...

    看起來沒什麼大不了, 不過這些都佔了我平常用電腦的大多數動作... 真是晴天霹靂... 還好是家裡的電腦, 愉樂用途居多, 如果是工作用的電腦, 我大概會抓狂... 比較起來, (2) 跟 (4) 對我影響最大, (2) 只要輸入中文的地方, 打起來都覺的怪怪的... 因為這幾個地方都不一樣了:

    • 最陽春的注音已經沒有底下一排的那種模式了
    • 從 DOS 時代就能用的 ALT+KEYPAD 輸入 ASCII 碼的功能也被拿掉了
      (我常習慣打 ALT-4, ALT-4 代表 ASCII 碼為 44 的字元: 逗號, 因為這樣不用切回英數模式)
    • 中文模式下按住SHIFT是可以輸入英文沒錯, 不過以前這樣打是小寫, 現在變大寫... [:@]...
    • 注音還停在選字的時後 (就字底下有虛線的狀態) 不能按 BACKSPACE 取消
      (打慣的我常因為這, 玩 WOW 邊打邊聊天就卡住, 法術按不出來就趴了... [:@])

    真是太讚了 [:@], 打到這邊已經打錯好幾個字, 十幾年來養成的習慣, 不知道要啥時才有可能習慣... Orz, 之前從windows2000就在耽心, 傳統的笨注音何時會被拿掉, 看來 vista 已經是第一步了... [:'(]

    而 (4), 不能裝 RAW Image Viewer, 連帶的讓我自己寫的歸檔程式都不能用 (因為我是靠它的 wrapper 才能讀取 canon raw file)... 真是... 查了一下 MicrosoftDN, 原來 vista 裡已經全面改用 WPF 處理, 因此原本內建的 image 相關軟體都換過了, 以 .NET 來說, 就是從原本的 GDI+ (System.Draw) 改到 WIC (Windows Imaging Component, System.Media.Imaging). 像 video 一樣, 針對各種不同的 image file 也改用 codec 的方式來處理了. 這也是 Microsoft 在 HD Photo 計劃裡重要的一環, 藉著 WPF 首次引入 HD Photo ( Windows Media Photo ), 目標是把 JPEG 換掉, 不過應該有好一段路要走.... 看起來以後會有較正式的 API 可以存取 RAW file, 不用像我之前一樣要走旁門... 不過, Nikon 已經提供了 NFE 檔的 WIC codec 下載, Canon 的 CRW 咧? [:'(] 看起來是該把歸檔程式用 WIC 改一下了... 只要 Canon Codec 快點出來...

    (5) 則是大名鼎鼎的 UAC, 可以預防管理者不知不覺做了啥危險的動作. 只要你執行的程式要做較危險的動作, 就會跳出來跟你確認一下... 用意很好, 不過實在有點煩人, 感覺好像以前用 AntiVirus, 我自己有一些 script 用 FileSystemObject 在做事, Norton AV 老跳出來警告我, 認為我寫的 script 是病毒... 現在又是一樣的感覺, 真是... 不過隨便 GOOGLE 查了一下, 沒想到賽門鐵克 (就是賣 AntiVirus 的公司) 還會放話說 UAC 不好, 真是, 要嘲笑鱉沒尾巴也不是這樣笑的....

    大概我碰 UNIX 比碰 window 來的早, 我還是比較習慣 sudo 的作法. sudo 是在你需要時, 才主動透過 sudo 暫時取得 root 的身份來做事. 平常用的帳號本來就不該有太大的權限. 從 NT 以來, 不用 administrator 登入, 跟本很難做事, 我曾經自己很安份的用 Power Users 身份的帳號工作了一陣子, 後來實在是受不了又換回來... 到了 2000 / xp 情況就好多了, 有很類似 sudo 的 runas 指令可以用, 也可以在捷徑上先指定好 runas 執行身份... ( 沒錯, 我把 DOS prompt 還有 MMC 拉了捷逕, 設成 administrator 身份開啟 ), 用 PowerUsers 身份就用的很快樂...

    不過碰到 UAC 真是一肚子火, 嘖嘖, 對知道的人是幫倒忙, 對不知道的人他也只會通通按 YES -_-, 就像所有的精靈都是按下一步一樣, 哈哈... 不過至少還是有進步, 畢竟不是每個人都有嫌工夫像我一樣設 RUNAS ..., 不過不習慣就是不習慣, 試沒多久就把 UAC 給關了.. [:$]

    牢騷發完了, 謝謝收看 [:D], 下回來寫點正面的...

    2007/04/03 技術隨筆 有的沒的

  4. 便宜的 SATA 光碟機

    太久沒自己 DIY PC 了, 連行情都一點概念也沒有... 我很少燒片, 所以舊的 4x 燒錄器就加減用, 不過沒光碟機就真的很麻煩, 因此想替我兩台 PC 買個便宜的 DVD-ROM 來用用... 現在 Intel 965 (ICH8) 的主機板跟本沒內建 IDE, 只有靠一顆外加的 chip 提供 IDE port, driver 又有點小問題, 之前被搞的很煩, 因此順手去 Y拍 查了一下 SATA 介面的 DVD-ROM, 沒想到真的有.... [:D], 兩台光碟機 + 運費 = NTD 1000, 二話不說就買了兩台..

    裝機前放桌上隨便拍了一張, 看來組裝 PC 都是拿這種來裝機出貨吧, 面板很乾淨, 除了一個燈號, 一個按鈕之外就沒有了, 背後也是, 就一個 SATA 訊號接頭, 加上一個 SATA 的電源接頭就沒了, 不像硬碟至少都還有傳統的電源接頭等等, cost down 真徹底...

    機身上都看不到啥牌子 or 型號等資訊, 賣家說是 BenQ 1640, 接上去從 BIOS / Windows Device Manager 看, 它標示的型號是 PHILIPS - DROM6316, google 查了一下, 原來 dell 出貨也搭這台, 咳咳...

    用起來沒啥特別的, 就光碟機嘛, 中規中矩的, 我手上的片子都還讀的出來. 有需要的朋友可以上 Y拍 找看看, 不想免費幫它打廣告, 真的想買又找不到再問我吧, 我給你 link ...

    2007/03/30 敗家 有的沒的

  5. Vista 初體驗 - (DISK篇)...

    Vista 試了兩天, 果然是有隱藏很多驚喜在裡頭. Microsoft 真是家不簡單的公司... 小地方我就不寫了, 我只寫對我比較特別的部份:

    [Volume Shadow Copy]

    因為我是把原本 file server 的工作都移到 desktop 上, 因此 vista 終於把 vss 納入, 真是福音一件啊 [:D], 原本我的 server 放照片的那區, 有雙重保護 ( RAID-1 + Volume Shadow Copy ), 換到 XP 時心裡很不安, 保護都沒有了. RAID 可以用主機版上面的取代, 不過可以預防不小心被吳小皮 or 老婆大人砍掉照片的 VSS 就沒有了. 昨天發現 Vista 有提供 VSS 真是感動啊... ( Microsoft 你什麼時後才要把 RAID-1 加進來...? 用主機板內建的 RAID 很可怕啊... )

     

    [Windows Complete PC]

    另一個驚喜, 是老早就知道的, 就是 vista 開始內建 disk image 的工具 ( windows complete pc, ultima 版限定, 像 ghost 那樣的東西 ), 這沒什麼特別的, 不過試用一下的發現才發現, 做出來的檔案格式, 就是 VHD ( virtual hard disk ).

    頓時覺的, Microsoft 真是間可怕的公司, 難怪他老是立於不敗之地. VHD 是 Microsoft 在 virtual machine 上面推廣的 virtuah hard disk 格式. Virtual PC / Virtual Server 都是以這個格式為 vm 的 disk. 一年多前才公開了 vhd 的 spec, 廣邀各方拿去使用, Virtual Server 2005 R2 SP1 也將會提供 VHD mount 的工具. 這樣佈局下來, ghost 早已不是對手了, 我還沒試過, 不過未來 vista pc 要移到 virtual machine 內應該就更簡單了, 只要做好 disk image, 再用 vm 掛起來, 搞定收工. 這麼讚的事一定要找時間來試一試.

     

    [Microsoft iSCSI Initiator]

    另一個驚喜, 是 vista 竟然已經內建 Microsoft iSCSI initiator... 這個倒沒什麼特別的, Microsoft 老早就 free download 了. 不過我覺的它背後的意義, 是不是代表 client 已經做好 iSCSI client 的準備, 未來 windows server 就會內建 iSCSI target ? 會的話, 格式就會直接用 .vhd ? Virtual PC / Virtual Server 就有進一步的機會直接從 iSCSI target mount disk 來 boot vm ? 哇哈哈, 之前我找半天才湊起來的 solution, 看來以後有望內建了 [Y]

     

    Microsoft 真是個不簡單的公司, 它總是很能利用 software 要 reuse 才有價值的理念, 打贏過去的每一場仗. 從當年的 office, 到 netscape, 到現在的 visualization solution, 還有 .net technology 都是... Microsoft 要跟 netscape 對打, 不是做出更好的 IE 內建而以. 它既然花了心力去打造 IE 這瀏覽器, Microsoft 一定會有 SDK, 讓到處都可以用的到 IE. 從 HTML Help, 檔案總管, windows 桌面, 到每一個小軟體都可以開網頁 (像 msn messenger 裡玩 game 就是), 還有一堆 Microsoft 的管理工具都看的到 IE 的影子, 除非你不用 Microsoft 的軟體了, 不然就算你用 FireFox, 你還是甩不掉 IE...

    其實這樣的例子很多, Microsoft 每一場重要的戰爭都是這樣贏的吧, 不知道 vista 裡還有什麼驚奇, 我再來研究看看... [:D]

    2007/03/28 Tips 技術隨筆 有的沒的