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 是错的, 跟本没编码 ...
浙公网安备 33010602011771号