2007/4/6 下午 03:01:00

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

.NET | TROUBLE SHOOTING | 小技巧 | 技術隨筆

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

 

   1:  MailMessage mail = new MailMessage();
   2:  Encoding chtEnc = Encoding.GetEncoding(950);
   3:  mail.From = new MailAddress("peter@chicken-house.net", "吳小皮", chtEnc);
   4:  mail.To.Add(new MailAddress("annie@chicken-house.net", "吳小妹", chtEnc));
   5:  mail.Subject = "今天天氣很好";
   6:  mail.SubjectEncoding = chtEnc;
   7:  mail.Body = "blah blah blah...";
   8:  (new SmtpClient()).Send(mail);

 

嗯, 執行的很好, 收的到 MAIL, 編碼也沒問題. 不過沒有回應的 code 總是不大 friendly, 加印一行 message 看看...

 

   1:  MailMessage mail = new MailMessage();
   2:  Encoding chtEnc = Encoding.GetEncoding(950);
   3:  mail.From = new MailAddress("peter@chicken-house.net", "吳小皮", chtEnc);
   4:  mail.To.Add(new MailAddress("annie@chicken-house.net", "吳小妹", chtEnc));
   5:  mail.Subject = "今天天氣很好";
   6:  mail.SubjectEncoding = chtEnc;
   7:  mail.Body = "blah blah blah...";
   8:  Console.WriteLine("準備寄信 (From: {0})", mail.From);
   9:  (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:

   1:  if (!MimeBasePart.IsAnsi(value, false))
   2:  {
   3:      throw new FormatException(SR.GetString("InvalidHeaderValue"));
   4:  }

 

怎麼看都沒問題, 追過 IsAnsi( ), 裡面沒啥特別的 code, 就 char 的值小於 0xff 就判定 pass. 所以問題應該出在 value 的值送進來判定時就已經有問題了... 再往上追, value 的源頭是 MailAddress 物件的 .ToEncodedString( ) 來的:

class: System.Net.Mail.MailAddress

   1:  internal string ToEncodedString()
   2:  {
   3:      if (this.fullAddress == null)
   4:      {
   5:          if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
   6:          {
   7:              StringBuilder builder = new StringBuilder();
   8:              MailBnfHelper.GetDotAtomOrQuotedString(this.encodedDisplayName, builder);
   9:              builder.Append(" <");
  10:              builder.Append(this.Address);
  11:              builder.Append('>');
  12:              this.fullAddress = builder.ToString();
  13:          }
  14:          else
  15:          {
  16:              this.fullAddress = this.Address;
  17:          }
  18:      }
  19:      return this.fullAddress;
  20:  }


然後跟加了就會出問題的 ToString( ) 比對著看:

   1:  public override string ToString()
   2:  {
   3:      if (this.fullAddress == null)
   4:      {
   5:          if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
   6:          {
   7:              StringBuilder builder = new StringBuilder();
   8:              builder.Append('"');
   9:              builder.Append(this.DisplayName);
  10:              builder.Append("\" <");
  11:              builder.Append(this.Address);
  12:              builder.Append('>');
  13:              this.fullAddress = builder.ToString();
  14:          }
  15:          else
  16:          {
  17:              this.fullAddress = this.Address;
  18:          }
  19:      }
  20:      return this.fullAddress;
  21:  }

 

Ouch, 真是想罵人, 問題就在這裡... 看起來是 M$ 工程師為了避開重複作編碼的動作, 每次呼叫 ToEncodedString( ) 及 ToString( ) 時都會去看看 fullAddress 這個 private field 是否有值? 有的話代表之前已經作過編碼了, 就直接撿現成. 問題出在第一次呼叫時, 編碼的動作在 ToString( ) 及 ToEncodedString( ) 各寫了一次 (果然沒有做好 refactoring ... 哈哈), 結果 ToString( ) 的這份 code implementation 是錯的, 跟本沒編碼 [:@] ... 我沒事雞婆在寄信前呼叫 ToString( ) 印 Messabe 算我運氣背, 就碰到這個 Bug.. [:@]

花了近兩天, 不過最後有找到 Bug, 而且還是 M$ 的 Bug, 哈哈, 總算證明人不是我殺的 [:D], submit M$ 的 Bug 有獎金嘛? 不只 Bug, 連問題都找到了... 有的話通知一下 [:D]



評論

新增評論


(將顯示您的Gravatar圖示)  

  Country flag

biuquote
Loading





Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen


RUN! PC 文章及範例下載

2008/11. 生產線模式的多執行緒應用
2008/09. 用ThreadPool發揮CPU運算能力
2008/06. SEMAPHORE在ASP.NET的應用
2008/04. 以ASP.NET開發同步WEB應用程式

安德魯是誰?

一堆人說我兒子跟我很像,就拿他的照片來頂替一下... :P

我喜歡鑽研物件導向、軟體工程及作業系統等相關技術。我會在這裡發表我的研究心得,也當作我自己的學習筆記。

Calendar

<<  一月 2009  >>
星期星期星期星期星期星期星期
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

Recent comments

Comment RSS