【问题标题】:Getting System.Net.Mail.MailMessage as a MemoryStream in .NET 4.5 beta在 .NET 4.5 beta 中获取 System.Net.Mail.MailMessage 作为 MemoryStream
【发布时间】:2022-01-05 01:19:21
【问题描述】:

因此,以下代码用于在 .NET 4 中获取 System.Net.Mail.MailMessage 对象作为 MemoryStream,但是随着 .NET 4.5 beta 的发布,会发生运行时异常。

Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
    ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
    object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
    MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
    sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null);

    .....
}

在 sendMethod.Invoke() 上发生运行时异常。

【问题讨论】:

  • 有什么异常(堆栈跟踪可能有帮助)?
  • 异常是这样的:System.Reflection.TargetParameterCountException:参数计数不匹配。
  • 您是否将 .NET 4 代码与 .NET 4.5 进行了比较,看看它们是否丢弃了 Send 的重载?这当然可以通过使用dynamic 更简单地处理。
  • 请看下面我的回答,他们在 Send 方法中添加了一个额外的布尔参数(allowUnicode)。

标签: c# .net-4.5


【解决方案1】:

设法弄清楚如何在 .NET 4.5 beta 中再次使用它。 MailMessage 中的私有 API Send() 方法已更改为:internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode)

请在下面找到更新的代码。

Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
    ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
    object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
    MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
    sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null);

    .....
}

【讨论】:

  • 这就是访问非公共方法所获得的 :)
  • 据我调查,没有其他方法可以获取 MailMessage 的 MemoryStream。甚至这里之前的帖子都使用了私有 API 方法,甚至来自 3rd 方供应商的示例也使用了这种私有 API 方法。如果有人知道使用公共 API 的方法,那么分享它会很有帮助:)
  • 嗨,我也有同样的问题,但 closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);... 有什么线索吗?谢谢
  • 注意,在 .NET 5 中,mailWriterContructor 在上述代码中仍为 null。新的构造函数是 (Stream, bool)。
【解决方案2】:

如果您不想使用不受支持的 hack 并且不介意额外的性能损失,这可能是有用的。

public static class MailMessageExtensions
    {
    public static string  RawMessage(this MailMessage m)
        {
        var smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory };

        using (var tempDir = new TemporaryDirectory())
            {
            smtpClient.PickupDirectoryLocation = tempDir.DirectoryPath;
            smtpClient.Send( m );
            var emlFile = Directory.GetFiles( smtpClient.PickupDirectoryLocation ).FirstOrDefault();
            if ( emlFile != null )
                {
                return File.ReadAllText( emlFile );
                }
            else
                return null;
            }
        return null;
        }

    }

class TemporaryDirectory : IDisposable
    {
    public TemporaryDirectory()
        {
        DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        Directory.CreateDirectory( DirectoryPath );
        }

    public string DirectoryPath { get; private set; }

    public void Dispose()
        {
        if ( Directory.Exists( DirectoryPath ) )
            Directory.Delete( DirectoryPath, true );
        }
    }

【讨论】:

  • 不错的解决方法,并且可能适用于其他 api 版本。谢谢!
  • 我发现使用这种技术的唯一问题是密件抄送目的地已从标题中删除。 technet.microsoft.com/en-us/library/bb124230.aspx#Anchor_3
  • 不错。但有一件事:如果 app.config 包含 smtp 设置,则必须覆盖这些设置。确保将主机设置为localhost,将凭据设置为null,将EnableSsl 设置为false
  • 这将是我的首选解决方案。唯一的问题是;因为我们在 Windows\TEMP 中创建了随机文件夹,从长远来看,Windows 会“允许”这样做吗?我的意思是,如果一直同时发送许多邮件,程序最终会因此而崩溃吗?
【解决方案3】:

用于检查我是否使用了额外的布尔值:

 If _sendMethod.GetParameters.Length = 2 Then
    _sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True}, Nothing)
 Else
    _sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True, True}, Nothing)
 End If

【讨论】:

    【解决方案4】:

    带有额外 TRUE 的建议解决方案效果很好。

    我在 VS2012 中运行我的项目时开始出现错误,即使我在所有库中都没有使用 .net 4.5 而是 4.0。

    该错误仅发生在您安装了 VS2012 的机器上,看起来 VS2012 在您调试时引用了 .net 4.5。当您在运行 .net 4.0 的客户端中部署和运行应用程序时,一切正常。

    因此:如果您运行 4.0 - 不要添加额外的 TRUE,如果您运行 4.5 添加它。

    【讨论】:

    • 是的,我也遇到了这个问题。必须设置编译器条件,以便我们可以调试代码并让它在生产中工作。
    【解决方案5】:

    我们与邮件消息转换斗争了很长时间。最终解决方案是使用MimeKit

    var memoryStream = new MemoryStream();
    var mimeMessage = MimeMessage.CreateFromMailMessage(message);
    mimeMessage.WriteTo(memoryStream);
    

    如果您使用上述方法,您将非常接近,并且它适用于大多数文化,但最终主题编码会打败您。

    【讨论】:

      【解决方案6】:

      对于那些在 .NET 5 中遇到 mailWriterContructor 成为 null 或面临 参数计数不匹配 异常的人,请仔细查看我的可用解决方案对于任何流。链接here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-13
        • 2011-12-13
        • 2012-10-06
        • 1970-01-01
        • 1970-01-01
        • 2013-12-04
        相关资源
        最近更新 更多