Is there any easy way to convert a System.Net.Mail.MailMessage object to the raw mail message text, like when you open a eml file in notepad.
5 Answers
Here's the same solution, but as an extension method to MailMessage.
Some of the reflection overhead is minimized by grabbing the ConstructorInfo and MethodInfo members once in the static context.
/// <summary> /// Uses reflection to get the raw content out of a MailMessage. /// </summary> public static class MailMessageExtensions { private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly Type MailWriter = typeof(SmtpClient).Assembly.GetType("System.Net.Mail.MailWriter"); private static readonly ConstructorInfo MailWriterConstructor = MailWriter.GetConstructor(Flags, null, new[] { typeof(Stream) }, null); private static readonly MethodInfo CloseMethod = MailWriter.GetMethod("Close", Flags); private static readonly MethodInfo SendMethod = typeof(MailMessage).GetMethod("Send", Flags); /// <summary> /// A little hack to determine the number of parameters that we /// need to pass to the SaveMethod. /// </summary> private static readonly bool IsRunningInDotNetFourPointFive = SendMethod.GetParameters().Length == 3; /// <summary> /// The raw contents of this MailMessage as a MemoryStream. /// </summary> /// <param name="self">The caller.</param> /// <returns>A MemoryStream with the raw contents of this MailMessage.</returns> public static MemoryStream RawMessage(this MailMessage self) { var result = new MemoryStream(); var mailWriter = MailWriterConstructor.Invoke(new object[] { result }); SendMethod.Invoke(self, Flags, null, IsRunningInDotNetFourPointFive ? new[] { mailWriter, true, true } : new[] { mailWriter, true }, null); result = new MemoryStream(result.ToArray()); CloseMethod.Invoke(mailWriter, Flags, null, new object[] { }, null); return result; } } To grab the underlying MemoryStream:
var email = new MailMessage(); using (var m = email.RawMessage()) { // do something with the raw message } 8 Comments
Send) in .NET 4.5 on your PC? I had to copy the content of result to another MemoryStream before calling CloseMethod, because the current implementation will actually close the underlying stream.I've implemented logic in MimeKit to allow you to cast a System.Net.Mail.MailMessage into a MimeKit.MimeMessage. Once you do that, you can simply write the message to a stream:
var message = (MimeMessage) CreateSystemNetMailMessage (); using (var stream = File.Create ("C:\\message.eml")) message.WriteTo (stream); This does not require reflecting into internal methods which means that it isn't dependent on the runtime, making it far more portable that the other answers given so far.
5 Comments
The code I've seen to do this relies on reflection. I adapted the samples found online to create this method:
private static MemoryStream ConvertMailMessageToMemoryStream(MailMessage message) { BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; Assembly assembly = typeof(SmtpClient).Assembly; MemoryStream stream = new MemoryStream(); Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(flags, null, new[] { typeof(Stream) }, null); object mailWriter = mailWriterContructor.Invoke(new object[] { stream }); MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", flags); sendMethod.Invoke(message, flags, null, new[] { mailWriter, true }, null); MethodInfo closeMethod = mailWriter.GetType().GetMethod("Close", flags); closeMethod.Invoke(mailWriter, flags, null, new object[] { }, null); return stream; } You can then convert the MemoryStream to a string or whatever you need.
Update: A method signature has changed in .NET 4.5, which breaks the above: Getting System.Net.Mail.MailMessage as a MemoryStream in .NET 4.5 beta
Comments
Fixing @allonhadaya's answer for .NET 8:
/// <summary> /// Uses reflection to get the raw content out of a MailMessage. /// </summary> public static class MailMessageExtensions { private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly Type? MailWriter = typeof(SmtpClient).Assembly.GetType("System.Net.Mail.MailWriter"); private static readonly ConstructorInfo? MailWriterConstructor = MailWriter?.GetConstructor(Flags, null, new[] { typeof(Stream), typeof(bool) }, null); private static readonly MethodInfo? CloseMethod = MailWriter?.GetMethod("Close", Flags); private static readonly MethodInfo? SendMethod = typeof(MailMessage).GetMethod("Send", Flags); /// <summary> /// A little hack to determine the number of parameters that we /// need to pass to the SaveMethod. /// </summary> private static readonly bool IsRunningInDotNetFourPointFive = SendMethod?.GetParameters().Length == 3; /// <summary> /// The raw contents of this MailMessage as a MemoryStream. /// </summary> /// <param name="self">The caller.</param> /// <returns>A MemoryStream with the raw contents of this MailMessage.</returns> public static MemoryStream RawMessage(this MailMessage self) { var result = new MemoryStream(); var mailWriter = MailWriterConstructor?.Invoke([result, false]); _ = mailWriter ?? throw new ApplicationException("Could not create a new MailWriter"); _ = SendMethod ?? throw new ApplicationException("Could not find the SendMethod"); SendMethod?.Invoke(self, Flags, null, IsRunningInDotNetFourPointFive ? new[] { mailWriter, true, true } : new[] { mailWriter, true }, null); result = new MemoryStream(result.ToArray()); _ = CloseMethod ?? throw new ApplicationException("Could not find the CloseMethod"); CloseMethod?.Invoke(mailWriter, Flags, null, [], null); return result; } } Changed is that the internal MailWriter constructor was changed to take one more bool encodeForTransport parameter (see https://stackoverflow.com/a/73398685/2003763).
Comments
byte[] allBytes = new byte[attachment.ContentStream.Length]; int bytesRead = attachment.ContentStream.Read(allBytes, 0, (int)attachment.ContentStream.Length); Encoding encoding = Encoding.UTF8; String contenidoCorreo = encoding.GetString(allBytes);