好的,
现在我知道如何重现这个问题以及如何解决它(或者更确切地说是如何避免它)。
要重现,请创建一个空的 .NET 3.5 控制台应用程序并复制以下代码。
namespace Example
{
class Program
{
private static SmtpClient _smtp = null;
public static SmtpClient Smtp
{
get
{
if (_smtp == null)
{
_smtp = new SmtpClient("relay.your-smtp-host.com", 25);
_smtp.UseDefaultCredentials = true;
}
return _smtp;
}
}
static void Main(string[] args)
{
using (MailMessage ms = new MailMessage())
{
var from = new MailAddress("from@email-address.com", "**THE NAME MUST CONTAINS SOME NON ANSI CHARS**");
string s = from.ToString(); /* This is the line - comment it and everything will work fine! */
ms.From = from;
ms.To.Add(new MailAddress("recipient@address.co.il"));
ms.Subject = "Here is my message title";
ms.Body = "Here is my message body";
Smtp.Send(ms);
}
}
}
}
笔记:
- 要重现此问题,您必须在 DisplayName 属性中放置一些非 ANSI 字符。
- 在没有断点的情况下运行该程序一次,然后注释行字符串 s = from.ToString()并再次运行。
如您所见,如果在调用 Smtp.Send 之前调用了“ToString”方法,则会引发异常。
好吧,我首先遇到了这个问题,因为我使用断点运行应用程序,所以调试器在后台调用 ToString() 方法。
(为了使事情更清楚,只需输入以下行:
System.Diagnostics.Debug.WriteLine(from)
在调用 Smtp.Send 之前,然后在调用之后移动它)。
问题:
这种奇怪行为的原因是什么?
回答:
结果表明,在.NET 框架 3.5中,MailAddress 类中有两种方法,它们以两种不同的方式设置名为“fullAddress”的内部字段。
第一种方法是 ToEncodedString():
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();
if (this.DisplayName.StartsWith("\"") && this.DisplayName.EndsWith("\""))
{
builder.Append(this.DisplayName);
}
else
{
builder.Append('"');
builder.Append(this.DisplayName);
builder.Append('"');
}
builder.Append(" <");
builder.Append(this.Address);
builder.Append('>');
this.fullAddress = builder.ToString();
}
else
{
this.fullAddress = this.Address;
}
}
return this.fullAddress;
}
现在,在 SmtpClient 中,当发送消息时,它会触发“From”标头的设置:
this.Headers[MailHeaderInfo.GetString(MailHeaderID.From)] = this.From.ToEncodedString();
这又会触发以下方法:
public override void Set(string name, string value)
{
if (Logging.On)
{
Logging.PrintInfo(Logging.Web, this, "Set", name.ToString() + "=" + value.ToString());
}
if (name == null)
{
throw new ArgumentNullException("name");
}
if (value == null)
{
throw new ArgumentNullException("value");
}
if (name == string.Empty)
{
throw new ArgumentException(SR.GetString("net_emptystringcall", new object[] { "name" }), "name");
}
if (value == string.Empty)
{
throw new ArgumentException(SR.GetString("net_emptystringcall", new object[] { "value" }), "name");
}
if (!MimeBasePart.IsAscii(name, false))
{
throw new FormatException(SR.GetString("InvalidHeaderName"));
}
if (!MimeBasePart.IsAnsi(value, false))
{
throw new FormatException(SR.GetString("InvalidHeaderValue"));
}
name = MailHeaderInfo.NormalizeCase(name);
MailHeaderID iD = MailHeaderInfo.GetID(name);
if ((iD == MailHeaderID.ContentType) && (this.part != null))
{
this.part.ContentType.Set(value.ToLower(CultureInfo.InvariantCulture), this);
}
else if ((iD == MailHeaderID.ContentDisposition) && (this.part is MimePart))
{
((MimePart) this.part).ContentDisposition.Set(value.ToLower(CultureInfo.InvariantCulture), this);
}
else
{
base.Set(name, value);
}
}
This method validates that the header value contains only ANSI character before sending it through the network (because of SMTP protocol limitations). But the "fullAddress" field was already set with non ANSI characters by a call to ToString(). Hence the sending was failed due to exception, saying that there are "Invalid" characters in the header.
After looking in the reflected code of .NET 4.0 it seems that this issue was solved by totally rewriting the internal implementation of MailAddress!
Thanks for trying to help!
P.S.
@ShadowWizard As you can see, sometimes there is nothing :-)